diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 21c6d21650ad6..8784e71447042 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -147,6 +147,7 @@ locations.
     ├─ config.sub                   # https://git.savannah.gnu.org/cgit/config.git
     ├─ libtool.m4                   # https://git.savannah.gnu.org/cgit/libtool.git
     ├─ ltmain.sh                    # https://git.savannah.gnu.org/cgit/libtool.git
+    ├─ pkg.m4                       # https://gitlab.freedesktop.org/pkg-config/pkg-config
     ├─ shtool                       # https://www.gnu.org/software/shtool/
     └─ ...
  ├─ docs/                           # PHP internals and repository documentation
diff --git a/NEWS b/NEWS
index 482088e0e09a8..c8acb4be5b3be 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,8 @@ PHP                                                                        NEWS
     function). (Nikita)
   . Fixed bug #53826 (__callStatic fired in base class through a parent call if
     the method is private). (Nikita)
+  . Implemented FR #77372 (Relative file path is removed from uploaded file).
+    (Björn Tantau)
 
 - Date:
   . Fixed bug #52480 (Incorrect difference using DateInterval) (Derick)
@@ -90,7 +92,9 @@ PHP                                                                        NEWS
     fetching a BLOB). (Nikita)
 
 . PDO MySQL:
-  . Fixed bug#80908 (PDO::lastInsertId() return wrong). (matt)
+  . Fixed bug #80908 (PDO::lastInsertId() return wrong). (matt)
+  . Fixed bug #81037 (PDO discards error message text from prepared
+    statement). (Kamil Tekiela)
 
 . PDO ODBC:
   . Implement PDO_ATTR_SERVER_VERSION and PDO_ATTR_SERVER_INFO for
diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c
index 2d60b6a9d6eee..a39564b8930d0 100644
--- a/TSRM/TSRM.c
+++ b/TSRM/TSRM.c
@@ -741,6 +741,14 @@ TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void)
 	asm ("leal _tsrm_ls_cache@ntpoff,%0"
           : "=r" (ret));
 	return ret;
+#elif defined(__aarch64__)
+	size_t ret;
+
+	asm("mov %0, xzr\n\t"
+	    "add %0, %0, #:tprel_hi12:_tsrm_ls_cache, lsl #12\n\t"
+	    "add %0, %0, #:tprel_lo12_nc:_tsrm_ls_cache"
+	     : "=r" (ret));
+	return ret;
 #else
 	return 0;
 #endif
diff --git a/UPGRADING b/UPGRADING
index 5a6c736e364eb..25d82303b30ce 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -57,6 +57,10 @@ PHP 8.1 UPGRADE NOTES
     This means that static variables in methods now behave the same way as
     static properties.
     RFC: https://wiki.php.net/rfc/static_variable_inheritance
+  . Most non-final internal methods now require overriding methods to declare a
+    compatible return type, otherwise a deprecated notice is emitted during
+    inheritance validation.
+    RFC: https://wiki.php.net/rfc/internal_method_return_types
 
 - Fileinfo:
   . The fileinfo functions now accept and return, respectively, finfo objects
@@ -176,6 +180,9 @@ PHP 8.1 UPGRADE NOTES
     RFC: https://wiki.php.net/rfc/noreturn_type
   . Added support for fibers.
     RFC: https://wiki.php.net/rfc/fibers
+  . File uploads now provide an additional full_path key, which contains the
+    full path (rather than just the basename) of the uploaded file. This is
+    intended for use in conjunction with "upload webkitdirectory".
 
 - Curl:
   . Added CURLOPT_DOH_URL option.
@@ -274,6 +281,12 @@ PHP 8.1 UPGRADE NOTES
 3. Changes in SAPI modules
 ========================================
 
+- CLI:
+  . Using -a without the readline extension will now result in an error.
+    Previously, -a without readline had the same behavior as calling php without
+    any arguments, apart from printing an additional "Interactive mode enabled"
+    message. This mode was not, in fact, interactive.
+
 ========================================
 4. Deprecated Functionality
 ========================================
diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c
index 6cbb5c3c4390b..74dcfbb99738a 100644
--- a/Zend/Optimizer/compact_literals.c
+++ b/Zend/Optimizer/compact_literals.c
@@ -112,6 +112,41 @@ static uint32_t add_static_slot(HashTable     *hash,
 	return ret;
 }
 
+static zend_string *create_str_cache_key(zval *literal, uint32_t flags)
+{
+	ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
+	uint32_t num_related = LITERAL_NUM_RELATED(flags);
+	if (num_related == 1) {
+		return zend_string_copy(Z_STR_P(literal));
+	}
+	if ((flags & LITERAL_KIND_MASK) == LITERAL_VALUE) {
+		/* Don't merge LITERAL_VALUE that has related literals */
+		return NULL;
+	}
+
+	/* Concatenate all the related literals for the cache key. */
+	zend_string *key;
+	if (num_related == 2) {
+		ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
+		key = zend_string_concat2(
+			Z_STRVAL_P(literal), Z_STRLEN_P(literal),
+			Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
+	} else if (num_related == 3) {
+		ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
+		key = zend_string_concat3(
+			Z_STRVAL_P(literal), Z_STRLEN_P(literal),
+			Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
+			Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
+	} else {
+		ZEND_ASSERT(0 && "Currently not needed");
+	}
+
+	/* Add a bias to the hash so we can distinguish keys
+	 * that would otherwise be the same after concatenation. */
+	ZSTR_H(key) = zend_string_hash_val(key) + num_related - 1;
+	return key;
+}
+
 void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
 {
 	zend_op *opline, *end;
@@ -403,16 +438,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
 					}
 					break;
 				case IS_STRING: {
-					if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
-						key = zend_string_copy(Z_STR(op_array->literals[i]));
-					} else if ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE) {
-						key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
-						ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i])) +
-							LITERAL_NUM_RELATED(info[i].flags) - 1;
-					} else {
-						/* Don't merge LITERAL_VALUE that has related literals */
-						key = NULL;
-					}
+					key = create_str_cache_key(&op_array->literals[i], info[i].flags);
 					if (key && (pos = zend_hash_find(&hash, key)) != NULL &&
 					    Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
 					    LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c
index 3542458f762c6..4ca4dc3810ae0 100644
--- a/Zend/Optimizer/zend_func_info.c
+++ b/Zend/Optimizer/zend_func_info.c
@@ -844,7 +844,7 @@ ZEND_API uint32_t zend_get_func_info(
 #endif
 
 		ret = zend_get_return_info_from_signature_only(
-			callee_func, /* script */ NULL, ce, ce_is_instanceof);
+			callee_func, /* script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
 
 #if ZEND_DEBUG
 		if (internal_ret) {
@@ -884,7 +884,7 @@ ZEND_API uint32_t zend_get_func_info(
 		}
 		if (!ret) {
 			ret = zend_get_return_info_from_signature_only(
-				callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof);
+				callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
 		}
 	}
 	return ret;
diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c
index 2326b8a21f03d..c582a8763ad98 100644
--- a/Zend/Optimizer/zend_inference.c
+++ b/Zend/Optimizer/zend_inference.c
@@ -4001,9 +4001,11 @@ static int is_recursive_tail_call(const zend_op_array *op_array,
 
 uint32_t zend_get_return_info_from_signature_only(
 		const zend_function *func, const zend_script *script,
-		zend_class_entry **ce, bool *ce_is_instanceof) {
+		zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info) {
 	uint32_t type;
-	if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+	if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE &&
+		(use_tentative_return_info || !ZEND_ARG_TYPE_IS_TENTATIVE(func->common.arg_info - 1))
+	) {
 		zend_arg_info *ret_info = func->common.arg_info - 1;
 		type = zend_fetch_arg_info_type(script, ret_info, ce);
 		*ce_is_instanceof = ce != NULL;
@@ -4025,15 +4027,15 @@ uint32_t zend_get_return_info_from_signature_only(
 ZEND_API void zend_init_func_return_info(
 	const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret)
 {
-	if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
-		zend_ssa_range tmp_range = {0, 0, 0, 0};
-		bool is_instanceof = false;
-		ret->type = zend_get_return_info_from_signature_only(
-			(zend_function *) op_array, script, &ret->ce, &is_instanceof);
-		ret->is_instanceof = is_instanceof;
-		ret->range = tmp_range;
-		ret->has_range = 0;
-	}
+	ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE));
+
+	zend_ssa_range tmp_range = {0, 0, 0, 0};
+	bool is_instanceof = false;
+	ret->type = zend_get_return_info_from_signature_only(
+		(zend_function *) op_array, script, &ret->ce, &is_instanceof, /* use_tentative_return_info */ 1);
+	ret->is_instanceof = is_instanceof;
+	ret->range = tmp_range;
+	ret->has_range = 0;
 }
 
 void zend_func_return_info(const zend_op_array   *op_array,
diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h
index 861262968111b..f9dbb405cc23c 100644
--- a/Zend/Optimizer/zend_inference.h
+++ b/Zend/Optimizer/zend_inference.h
@@ -271,7 +271,7 @@ ZEND_API void zend_init_func_return_info(
 	const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret);
 uint32_t zend_get_return_info_from_signature_only(
 		const zend_function *func, const zend_script *script,
-		zend_class_entry **ce, bool *ce_is_instanceof);
+		zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info);
 void zend_func_return_info(const zend_op_array   *op_array,
                            const zend_script     *script,
                            int                    recursive,
diff --git a/Zend/Zend.m4 b/Zend/Zend.m4
index ca6768d2d6c54..d4dcd5459078b 100644
--- a/Zend/Zend.m4
+++ b/Zend/Zend.m4
@@ -278,7 +278,6 @@ int main()
   AC_DEFINE_UNQUOTED(ZEND_MM_ALIGNMENT_LOG2, $LIBZEND_MM_ALIGN_LOG2, [ ])
 ], [], [
   dnl Cross compilation needs something here.
-  LIBZEND_MM_ALIGN=8
   AC_DEFINE_UNQUOTED(ZEND_MM_ALIGNMENT, 8, [ ])
   AC_DEFINE_UNQUOTED(ZEND_MM_ALIGNMENT_LOG2, 3, [ ])
 ])
diff --git a/Zend/tests/attributes/031_backtrace.phpt b/Zend/tests/attributes/031_backtrace.phpt
index b0374e67e2fc8..504e356e7cc8d 100644
--- a/Zend/tests/attributes/031_backtrace.phpt
+++ b/Zend/tests/attributes/031_backtrace.phpt
@@ -19,8 +19,8 @@ class Test {}
 
 ?>
 --EXPECTF--
-#0  MyAttribute->__construct() called at [%s031_backtrace.php:12]
-#1  ReflectionAttribute->newInstance() called at [%s:%d]
+#0 %s031_backtrace.php(12): MyAttribute->__construct()
+#1 %s(%d): ReflectionAttribute->newInstance()
 array(2) {
   [0]=>
   array(7) {
diff --git a/Zend/tests/bug29896.phpt b/Zend/tests/bug29896.phpt
index b32a25e8d94c4..f0e25b716a4a4 100644
--- a/Zend/tests/bug29896.phpt
+++ b/Zend/tests/bug29896.phpt
@@ -22,6 +22,6 @@ function GenerateError2($A1)
 GenerateError2("Test2");
 ?>
 --EXPECTF--
-#0  userErrorHandler(2, Undefined variable $b, %s, %d) called at [%s:%d]
-#1  GenerateError1(Test1) called at [%sbug29896.php:16]
-#2  GenerateError2(Test2) called at [%sbug29896.php:19]
+#0 %s(%d): userErrorHandler(2, 'Undefined varia...', '%s', %d)
+#1 %s(%d): GenerateError1('Test1')
+#2 %s(%d): GenerateError2('Test2')
diff --git a/Zend/tests/bug30828.phpt b/Zend/tests/bug30828.phpt
index 4e921403b7f84..9b7e6eac3ca97 100644
--- a/Zend/tests/bug30828.phpt
+++ b/Zend/tests/bug30828.phpt
@@ -47,15 +47,15 @@ $b->foo();
 B::bar();
 ?>
 --EXPECTF--
-#0  A->__construct() called at [%sbug30828.php:30]
-#1  B->__construct() called at [%sbug30828.php:42]
+#0 %sbug30828.php(30): A->__construct()
+#1 %sbug30828.php(42): B->__construct()
 A->__construct
 B->__construct
-#0  A->foo() called at [%sbug30828.php:34]
-#1  B->foo() called at [%sbug30828.php:43]
+#0 %sbug30828.php(34): A->foo()
+#1 %sbug30828.php(43): B->foo()
 A->foo
 B->foo
-#0  A::bar() called at [%sbug30828.php:38]
-#1  B::bar() called at [%sbug30828.php:44]
+#0 %sbug30828.php(38): A::bar()
+#1 %sbug30828.php(44): B::bar()
 A::bar
 B::bar
diff --git a/Zend/tests/bug40236.phpt b/Zend/tests/bug40236.phpt
index 204b117099f03..35ed84066b8b0 100644
--- a/Zend/tests/bug40236.phpt
+++ b/Zend/tests/bug40236.phpt
@@ -10,7 +10,5 @@ $php = getenv('TEST_PHP_EXECUTABLE');
 $cmd = "\"$php\" -n -d memory_limit=4M -a \"".__DIR__."\"/bug40236.inc";
 echo `$cmd`;
 ?>
---EXPECTF--
-Interactive %s
-
-ok
+--EXPECT--
+Interactive shell (-a) requires the readline extension.
diff --git a/Zend/tests/bug50146.phpt b/Zend/tests/bug50146.phpt
index 0ef9048b0613b..3d3b54ffc1e9e 100644
--- a/Zend/tests/bug50146.phpt
+++ b/Zend/tests/bug50146.phpt
@@ -13,11 +13,7 @@ var_dump($ref->hasProperty('b'));
 var_dump(isset($obj->a));
 
 ?>
---EXPECTF--
+--EXPECT--
+bool(false)
 bool(false)
 bool(false)
-
-Fatal error: Uncaught Error: Closure object cannot have properties in %s:%d
-Stack trace:
-#0 {main}
-  thrown in %s on line %d
diff --git a/Zend/tests/bug64239_3.phpt b/Zend/tests/bug64239_3.phpt
index 1a7da60e3c656..1de8cede02f70 100644
--- a/Zend/tests/bug64239_3.phpt
+++ b/Zend/tests/bug64239_3.phpt
@@ -27,7 +27,7 @@ $c->Bmethod();
 $c->t2method();
 ?>
 --EXPECTF--
-#0  A->Bmethod() called at [%sbug64239_3.php:%d]
-#0  A->t2method() called at [%sbug64239_3.php:%d]
-#0  C->Bmethod() called at [%sbug64239_3.php:%d]
-#0  A->t2method() called at [%sbug64239_3.php:%d]
+#0 %s(%d): A->Bmethod()
+#0 %s(%d): A->t2method()
+#0 %s(%d): C->Bmethod()
+#0 %s(%d): A->t2method()
diff --git a/Zend/tests/bug64239_4.phpt b/Zend/tests/bug64239_4.phpt
index 8204a5b8432bc..086a33bb48d9b 100644
--- a/Zend/tests/bug64239_4.phpt
+++ b/Zend/tests/bug64239_4.phpt
@@ -25,7 +25,7 @@ C::Bmethod();
 C::t2method();
 ?>
 --EXPECTF--
-#0  A::Bmethod() called at [%sbug64239_4.php:%d]
-#0  A::t2method() called at [%sbug64239_4.php:%d]
-#0  C::Bmethod() called at [%sbug64239_4.php:%d]
-#0  A::t2method() called at [%sbug64239_4.php:%d]
+#0 %s(%d): A::Bmethod()
+#0 %s(%d): A::t2method()
+#0 %s(%d): C::Bmethod()
+#0 %s(%d): A::t2method()
diff --git a/Zend/tests/bug70156.phpt b/Zend/tests/bug70156.phpt
index 0d20b38181255..be98277548275 100644
--- a/Zend/tests/bug70156.phpt
+++ b/Zend/tests/bug70156.phpt
@@ -32,6 +32,6 @@ class dummy {
 new dummy();
 ?>
 --EXPECTF--
-#0  dummy->bar() called at [%sbug70156.php:%d]
-#1  dummy->foo1() called at [%sbug70156.php:%d]
-#2  dummy->__construct() called at [%sbug70156.php:%d]
+#0 %s(%d): dummy->bar()
+#1 %s(%d): dummy->foo1()
+#2 %s(%d): dummy->__construct()
diff --git a/Zend/tests/bug73916.phpt b/Zend/tests/bug73916.phpt
index 7b51f8f31a38a..aa765a96aac2e 100644
--- a/Zend/tests/bug73916.phpt
+++ b/Zend/tests/bug73916.phpt
@@ -13,4 +13,4 @@ function test() {
 }
 ?>
 --EXPECTF--
-#0  test(Array ([0] => Array ([0] => a),[1] => b Object ())) called at [%sbug73916.php:%d]
+#0 %s(%d): test(Array)
diff --git a/Zend/tests/bug78973.phpt b/Zend/tests/bug78973.phpt
index 688f4e6cc54e0..f786636bfddfe 100644
--- a/Zend/tests/bug78973.phpt
+++ b/Zend/tests/bug78973.phpt
@@ -15,4 +15,4 @@ test(new class {
 
 ?>
 --EXPECTF--
-#0  class@anonymous->__destruct() called at [%s:%d]
+#0 %s(%d): class@anonymous->__destruct()
diff --git a/Zend/tests/bug_debug_backtrace.phpt b/Zend/tests/bug_debug_backtrace.phpt
index 95530e01e46ba..53f108ced76d5 100644
--- a/Zend/tests/bug_debug_backtrace.phpt
+++ b/Zend/tests/bug_debug_backtrace.phpt
@@ -19,8 +19,8 @@ eval("foo();");
 echo "Done\n";
 ?>
 --EXPECTF--
-#0  boo() called at [%s:%d]
-#1  bar() called at [%s:%d]
-#2  foo() called at [%s(%d) : eval()'d code:1]
-#3  eval() called at [%s:%d]
+#0 %s(%d): boo()
+#1 %s(%d): bar()
+#2 %s(%d) : eval()'d code(1): foo()
+#3 %s(%d): eval()
 Done
diff --git a/Zend/tests/call_user_func_005.phpt b/Zend/tests/call_user_func_005.phpt
index 2f5220db6291b..6a32f1970b563 100644
--- a/Zend/tests/call_user_func_005.phpt
+++ b/Zend/tests/call_user_func_005.phpt
@@ -18,7 +18,7 @@ var_dump(call_user_func(array('foo', 'teste')));
 
 ?>
 --EXPECTF--
-Deprecated: Required parameter $b follows optional parameter $a in %s on line %d
+Deprecated: Optional parameter $a declared before required parameter $b is implicitly treated as a required parameter in %s on line %d
 string(1) "x"
 array(1) {
   [0]=>
diff --git a/Zend/tests/closure_022.phpt b/Zend/tests/closure_022.phpt
index 8977e963dface..8e0fd06c1d329 100644
--- a/Zend/tests/closure_022.phpt
+++ b/Zend/tests/closure_022.phpt
@@ -8,7 +8,7 @@ $foo = function() use ($a) {
 $foo->a = 1;
 ?>
 --EXPECTF--
-Fatal error: Uncaught Error: Closure object cannot have properties in %sclosure_022.php:5
+Fatal error: Uncaught Error: Cannot create dynamic property Closure::$a in %s:%d
 Stack trace:
 #0 {main}
   thrown in %sclosure_022.php on line 5
diff --git a/Zend/tests/closure_031.phpt b/Zend/tests/closure_031.phpt
index e757f677488ef..19f3dc6e3212e 100644
--- a/Zend/tests/closure_031.phpt
+++ b/Zend/tests/closure_031.phpt
@@ -3,7 +3,7 @@ Closure 031: Closure properties with custom error handlers
 --FILE--
 <?php
 function foo($errno, $errstr, $errfile, $errline) {
-    echo "Error: $errstr\n";
+    echo "Warning: $errstr\n";
 }
 set_error_handler('foo');
 $foo = function() {
@@ -15,4 +15,5 @@ try {
 }
 ?>
 --EXPECT--
-Error: Closure object cannot have properties
+Warning: Undefined property: Closure::$a
+NULL
diff --git a/Zend/tests/closure_032.phpt b/Zend/tests/closure_032.phpt
index 601db15989b31..bf055c22856e2 100644
--- a/Zend/tests/closure_032.phpt
+++ b/Zend/tests/closure_032.phpt
@@ -29,7 +29,7 @@ Array
         )
 
 )
-#0  {closure}(23) called at [%s:%d]
+#0 %s(%d): {closure}(23)
 Array
 (
     [0] => Array
@@ -65,5 +65,5 @@ Array
         )
 
 )
-#0  {closure}(23) called at [%s:%d]
-#1  test(Closure Object ()) called at [%s:%d]
+#0 %s(%d): {closure}(23)
+#1 %s(%d): test(Object(Closure))
diff --git a/Zend/tests/closure_write_prop.phpt b/Zend/tests/closure_write_prop.phpt
index 38bebf4e1b786..8dbf18e670418 100644
--- a/Zend/tests/closure_write_prop.phpt
+++ b/Zend/tests/closure_write_prop.phpt
@@ -19,4 +19,4 @@ try {
 
 ?>
 --EXPECT--
-Closure object cannot have properties
+Cannot create dynamic property Closure::$b
diff --git a/Zend/tests/debug_backtrace_options.phpt b/Zend/tests/debug_backtrace_options.phpt
index 94842279ea979..460bbe3936ff7 100644
--- a/Zend/tests/debug_backtrace_options.phpt
+++ b/Zend/tests/debug_backtrace_options.phpt
@@ -45,29 +45,29 @@ foo::statCall("doit", "backtrace_print");
 ?>
 --EXPECTF--
 ==default
-#0  doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#1  foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#2  foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
+#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
+#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
+#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
 ==true
-#0  doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#1  foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#2  foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
+#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
+#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
+#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
 ==false
-#0  doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#1  foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#2  foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
+#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
+#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
+#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
 ==DEBUG_BACKTRACE_PROVIDE_OBJECT
-#0  doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#1  foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
-#2  foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
+#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
+#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
+#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
 ==DEBUG_BACKTRACE_IGNORE_ARGS
-#0  doit() called at [%sdebug_backtrace_options.php:%d]
-#1  foo->doCall() called at [%sdebug_backtrace_options.php:%d]
-#2  foo::statCall() called at [%sdebug_backtrace_options.php:%d]
+#0 %sdebug_backtrace_options.php(%d): doit()
+#1 %sdebug_backtrace_options.php(%d): foo->doCall()
+#2 %sdebug_backtrace_options.php(%d): foo::statCall()
 ==both
-#0  doit() called at [%sdebug_backtrace_options.php:%d]
-#1  foo->doCall() called at [%sdebug_backtrace_options.php:%d]
-#2  foo::statCall() called at [%sdebug_backtrace_options.php:%d]
+#0 %sdebug_backtrace_options.php(%d): doit()
+#1 %sdebug_backtrace_options.php(%d): foo->doCall()
+#2 %sdebug_backtrace_options.php(%d): foo::statCall()
 ==default
 Array
 (
diff --git a/Zend/tests/debug_print_backtrace_from_main.phpt b/Zend/tests/debug_print_backtrace_from_main.phpt
index 82f75742c774f..f6ea383c397e4 100644
--- a/Zend/tests/debug_print_backtrace_from_main.phpt
+++ b/Zend/tests/debug_print_backtrace_from_main.phpt
@@ -5,3 +5,4 @@ Calling debug_print_backtrace() from main script
 debug_print_backtrace();
 ?>
 --EXPECT--
+
diff --git a/Zend/tests/debug_print_backtrace_limit.phpt b/Zend/tests/debug_print_backtrace_limit.phpt
index 5100387bd6eed..e0d791cc7ba57 100644
--- a/Zend/tests/debug_print_backtrace_limit.phpt
+++ b/Zend/tests/debug_print_backtrace_limit.phpt
@@ -12,20 +12,26 @@ function b() {
 
 function c() {
     debug_print_backtrace(0, 1);
+    echo "\n";
     debug_print_backtrace(0, 2);
+    echo "\n";
     debug_print_backtrace(0, 0);
+    echo "\n";
     debug_print_backtrace(0, 4);
 }
 
 a();
 ?>
 --EXPECTF--
-#0  c() called at [%sdebug_print_backtrace_limit.php:7]
-#0  c() called at [%sdebug_print_backtrace_limit.php:7]
-#1  b() called at [%sdebug_print_backtrace_limit.php:3]
-#0  c() called at [%sdebug_print_backtrace_limit.php:7]
-#1  b() called at [%sdebug_print_backtrace_limit.php:3]
-#2  a() called at [%sdebug_print_backtrace_limit.php:17]
-#0  c() called at [%sdebug_print_backtrace_limit.php:7]
-#1  b() called at [%sdebug_print_backtrace_limit.php:3]
-#2  a() called at [%sdebug_print_backtrace_limit.php:17]
+#0 %sdebug_print_backtrace_limit.php(7): c()
+
+#0 %sdebug_print_backtrace_limit.php(7): c()
+#1 %sdebug_print_backtrace_limit.php(3): b()
+
+#0 %sdebug_print_backtrace_limit.php(7): c()
+#1 %sdebug_print_backtrace_limit.php(3): b()
+#2 %sdebug_print_backtrace_limit.php(20): a()
+
+#0 %sdebug_print_backtrace_limit.php(7): c()
+#1 %sdebug_print_backtrace_limit.php(3): b()
+#2 %sdebug_print_backtrace_limit.php(20): a()
diff --git a/Zend/tests/fibers/debug-backtrace.phpt b/Zend/tests/fibers/debug-backtrace.phpt
index bb37d4f5d0ec0..ef5ae8c9f0902 100644
--- a/Zend/tests/fibers/debug-backtrace.phpt
+++ b/Zend/tests/fibers/debug-backtrace.phpt
@@ -16,6 +16,6 @@ $fiber->start();
 
 ?>
 --EXPECTF--
-#0  inner_function() called at [%sdebug-backtrace.php:9]
-#1  {closure}()
-#2  Fiber->start() called at [%sdebug-backtrace.php:12]
+#0 %sdebug-backtrace.php(9): inner_function()
+#1 [internal function]: {closure}()
+#2 %sdebug-backtrace.php(12): Fiber->start()
diff --git a/Zend/tests/generators/backtrace.phpt b/Zend/tests/generators/backtrace.phpt
index 5fed1d467e676..816d06ba12d9b 100644
--- a/Zend/tests/generators/backtrace.phpt
+++ b/Zend/tests/generators/backtrace.phpt
@@ -21,7 +21,7 @@ f3($gen);
 
 ?>
 --EXPECTF--
-#0  f1() called at [%s:%d]
-#1  f2(foo, bar)
-#2  Generator->rewind() called at [%s:%d]
-#3  f3(Generator Object ()) called at [%s:%d]
+#0 %s(%d): f1()
+#1 [internal function]: f2('foo', 'bar')
+#2 %s(%d): Generator->rewind()
+#3 %s(%d): f3(Object(Generator))
diff --git a/Zend/tests/generators/backtrace_multi_yield_from.phpt b/Zend/tests/generators/backtrace_multi_yield_from.phpt
index 6627fe8458a42..4fbaa2f05bb49 100644
--- a/Zend/tests/generators/backtrace_multi_yield_from.phpt
+++ b/Zend/tests/generators/backtrace_multi_yield_from.phpt
@@ -26,7 +26,7 @@ var_dump($gen2->current());
 --EXPECTF--
 int(1)
 int(1)
-#0  gen() called at [%s:10]
-#1  from(Generator Object ())
-#2  Generator->next() called at [%s:19]
+#0 %s(10): gen()
+#1 [internal function]: from(Object(Generator))
+#2 %s(19): Generator->next()
 int(2)
diff --git a/Zend/tests/generators/yield_from_backtrace.phpt b/Zend/tests/generators/yield_from_backtrace.phpt
index 8fb1aeef8cb19..c92f80f4ca736 100644
--- a/Zend/tests/generators/yield_from_backtrace.phpt
+++ b/Zend/tests/generators/yield_from_backtrace.phpt
@@ -28,21 +28,21 @@ for ($gen = gen(); $gen->valid(); $gen->next()) {
 --EXPECTF--
 Implicit foreach:
 int(1)
-#0  gen() called at [%s:%d]
+#0 %s(%d): gen()
 int(2)
-#0  from(2) called at [%s:%d]
-#1  gen() called at [%s:%d]
+#0 %s(%d): from(2)
+#1 %s(%d): gen()
 int(3)
-#0  gen() called at [%s:%d]
+#0 %s(%d): gen()
 
 Explicit iterator:
 int(1)
-#0  gen()
-#1  Generator->next() called at [%s:%d]
+#0 [internal function]: gen()
+#1 %s(%d): Generator->next()
 int(2)
-#0  from(2) called at [%s:%d]
-#1  gen()
-#2  Generator->next() called at [%s:%d]
+#0 %s(%d): from(2)
+#1 [internal function]: gen()
+#2 %s(%d): Generator->next()
 int(3)
-#0  gen()
-#1  Generator->next() called at [%s:%d]
+#0 [internal function]: gen()
+#1 %s(%d): Generator->next()
diff --git a/Zend/tests/named_params/backtrace.phpt b/Zend/tests/named_params/backtrace.phpt
index 1ccae8503de6f..89d3c00ee8c3f 100644
--- a/Zend/tests/named_params/backtrace.phpt
+++ b/Zend/tests/named_params/backtrace.phpt
@@ -40,7 +40,7 @@ array(1) {
     }
   }
 }
-#0  test(1, 2, x: 3, y: 4) called at [%s:10]
+#0 %s(10): test(1, 2, x: 3, y: 4)
 array(1) {
   [0]=>
   array(4) {
diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt
index 69e7607bb350c..fc354a3855715 100644
--- a/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt
+++ b/Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt
@@ -4,10 +4,10 @@ The default value is a class constant in the parent class method's signature.
 <?php
 class MyDateTimeZone extends DateTimeZone
 {
-    public static function listIdentifiers()
+    public static function listIdentifiers(): array
     {
     }
 }
 ?>
 --EXPECTF--
-Fatal error: Declaration of MyDateTimeZone::listIdentifiers() must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) in %s on line %d
+Fatal error: Declaration of MyDateTimeZone::listIdentifiers(): array must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d
diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt
index 9335abd5d6a91..5b0a9bf05f0f0 100644
--- a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt
+++ b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt
@@ -4,10 +4,10 @@ The default value is a constant in the parent class method's signature.
 <?php
 class MyDateTimeZone extends DateTimeZone
 {
-    public function getTransitions()
+    public function getTransitions(): array|false
     {
     }
 }
 ?>
 --EXPECTF--
-Fatal error: Declaration of MyDateTimeZone::getTransitions() must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX) in %s on line %d
+Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false in %s on line %d
diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt
index 80c98a405253a..c01d7d256a66d 100644
--- a/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt
+++ b/Zend/tests/parameter_default_values/internal_declaration_error_false.phpt
@@ -5,8 +5,8 @@ The default value is false in the parent class method's signature.
 
 interface MyDateTimeInterface extends DateTimeInterface
 {
-    public function diff();
+    public function diff(): DateInterval;
 }
 ?>
 --EXPECTF--
-Fatal error: Declaration of MyDateTimeInterface::diff() must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false) in %s on line %d
+Fatal error: Declaration of MyDateTimeInterface::diff(): DateInterval must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval in %s on line %d
diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt
index c32cc9e41fbc1..edf71c0263fea 100644
--- a/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt
+++ b/Zend/tests/parameter_default_values/internal_declaration_error_int.phpt
@@ -4,10 +4,10 @@ The default value is an integer in the parent class method's signature.
 <?php
 class MyDateTime extends DateTime
 {
-    public function setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false)
+    public function setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false): DateTime
     {
     }
 }
 ?>
 --EXPECTF--
-Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false) must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) in %s on line %d
+Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false): DateTime must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime in %s on line %d
diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt
index 3804c2a6e1557..ebe5e142155ac 100644
--- a/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt
+++ b/Zend/tests/parameter_default_values/internal_declaration_error_null.phpt
@@ -4,10 +4,10 @@ The default value is null in the parent class method's signature.
 <?php
 class MyDateTime extends DateTime
 {
-    public static function createFromFormat()
+    public static function createFromFormat(): DateTime|false
     {
     }
 }
 ?>
 --EXPECTF--
-Fatal error: Declaration of MyDateTime::createFromFormat() must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) in %s on line %d
+Fatal error: Declaration of MyDateTime::createFromFormat(): DateTime|false must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false in %s on line %d
diff --git a/Zend/tests/required_param_after_optional.phpt b/Zend/tests/required_param_after_optional.phpt
index cd715e77d4819..c3a925efe88ac 100644
--- a/Zend/tests/required_param_after_optional.phpt
+++ b/Zend/tests/required_param_after_optional.phpt
@@ -5,10 +5,14 @@ Required parameter after optional is deprecated
 
 function test($testA = null, $testB = null, $testC) {}
 function test2(Type $test2A = null, $test2B = null, $test2C) {}
-function test3(Type $test3A = null, Type2 $test3B = null, $test3C) {}
+function test3(Type $test3A = null, ?Type2 $test3B = null, $test3C) {}
 
 ?>
 --EXPECTF--
-Deprecated: Required parameter $testC follows optional parameter $testA in %s on line %d
+Deprecated: Optional parameter $testA declared before required parameter $testC is implicitly treated as a required parameter in %s on line %d
 
-Deprecated: Required parameter $test2C follows optional parameter $test2B in %s on line %d
+Deprecated: Optional parameter $testB declared before required parameter $testC is implicitly treated as a required parameter in %s on line %d
+
+Deprecated: Optional parameter $test2B declared before required parameter $test2C is implicitly treated as a required parameter in %s on line %d
+
+Deprecated: Optional parameter $test3B declared before required parameter $test3C is implicitly treated as a required parameter in %s on line %d
diff --git a/Zend/tests/required_param_after_optional_named_args.phpt b/Zend/tests/required_param_after_optional_named_args.phpt
new file mode 100644
index 0000000000000..e504d76c7efed
--- /dev/null
+++ b/Zend/tests/required_param_after_optional_named_args.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Optional param before required should be treated as required for named args as well
+--FILE--
+<?php
+
+function test($a = 1, $b) {
+}
+try {
+    test(b: 2);
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECTF--
+Deprecated: Optional parameter $a declared before required parameter $b is implicitly treated as a required parameter in %s on line %d
+test(): Argument #1 ($a) not passed
diff --git a/Zend/tests/type_declarations/variance/internal_parent.phpt b/Zend/tests/type_declarations/variance/internal_parent.phpt
deleted file mode 100644
index c82e10ffc34d5..0000000000000
--- a/Zend/tests/type_declarations/variance/internal_parent.phpt
+++ /dev/null
@@ -1,12 +0,0 @@
---TEST--
-Internal class as parent
---FILE--
-<?php
-
-class Test extends DateTime {
-    public static function createFromFormat($format, $datetime, Wrong $timezone = null) { }
-}
-
-?>
---EXPECTF--
-Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, ?Wrong $timezone = null) and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null), because class Wrong is not available in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/internal_parent/compatible_return_type.phpt b/Zend/tests/type_declarations/variance/internal_parent/compatible_return_type.phpt
new file mode 100644
index 0000000000000..a45e3f4eb942f
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/internal_parent/compatible_return_type.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Test that no notice is emitted when the return type/value of the overriding method is compatible with the tentative return type/value of the overridden method
+--FILE--
+<?php
+class MyDateTimeZone extends DateTimeZone
+{
+    public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array
+    {
+        return [];
+    }
+}
+
+var_dump(MyDateTimeZone::listIdentifiers());
+?>
+--EXPECT--
+array(0) {
+}
diff --git a/Zend/tests/type_declarations/variance/internal_parent/incompatible_return_type.phpt b/Zend/tests/type_declarations/variance/internal_parent/incompatible_return_type.phpt
new file mode 100644
index 0000000000000..8c40a46a86608
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/internal_parent/incompatible_return_type.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Test that a notice is emitted when the return type/value of the overriding method is incompatible with the tentative return type/value of the overridden method
+--FILE--
+<?php
+class MyDateTimeZone extends DateTimeZone
+{
+    public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string
+    {
+        return "";
+    }
+}
+
+var_dump(MyDateTimeZone::listIdentifiers());
+?>
+--EXPECTF--
+Deprecated: Declaration of MyDateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string should be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d
+string(0) ""
diff --git a/Zend/tests/type_declarations/variance/internal_parent/missing_return_type.phpt b/Zend/tests/type_declarations/variance/internal_parent/missing_return_type.phpt
new file mode 100644
index 0000000000000..c20255df15c56
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/internal_parent/missing_return_type.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Test that a notice is emitted when the tentative return type of the overridden method is omitted
+--FILE--
+<?php
+class MyDateTimeZone extends DateTimeZone
+{
+    public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null)
+    {
+    }
+}
+?>
+--EXPECTF--
+Deprecated: Declaration of MyDateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) should be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_param.phpt b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_param.phpt
new file mode 100644
index 0000000000000..d1a1ad78ce30a
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_param.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test unresolvable inheritance check due to unavailable parameter type when the parent has a tentative return type.
+--FILE--
+<?php
+
+class Test extends DateTime {
+    public static function createFromFormat($format, $datetime, Wrong $timezone = null): DateTime|false {}
+}
+
+?>
+--EXPECTF--
+Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, ?Wrong $timezone = null): DateTime|false and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_return.phpt b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_return.phpt
new file mode 100644
index 0000000000000..5cc1730741920
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/internal_parent/unresolvable_inheritance_check_return.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test unresolvable inheritance check due to unavailable return type when the parent has a tentative return type.
+--FILE--
+<?php
+
+class Test extends DateTime {
+    public static function createFromFormat($format, $datetime, $timezone = null): Wrong { }
+}
+
+?>
+--EXPECTF--
+Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, $timezone = null): Wrong and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/return_type_will_change_class_error.phpt b/Zend/tests/type_declarations/variance/return_type_will_change_class_error.phpt
new file mode 100644
index 0000000000000..f2698bb7a9563
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/return_type_will_change_class_error.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Test that the ReturnTypeWillChange attribute cannot target classes
+--FILE--
+<?php
+
+#[ReturnTypeWillChange]
+class Foo
+{
+}
+
+?>
+--EXPECTF--
+Fatal error: Attribute "ReturnTypeWillChange" cannot target class (allowed targets: method) in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/return_type_will_change_function_error.phpt b/Zend/tests/type_declarations/variance/return_type_will_change_function_error.phpt
new file mode 100644
index 0000000000000..e539275c3e862
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/return_type_will_change_function_error.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Test that the ReturnTypeWillChange attribute cannot target functions
+--FILE--
+<?php
+
+#[ReturnTypeWillChange]
+function foo() {}
+
+?>
+--EXPECTF--
+Fatal error: Attribute "ReturnTypeWillChange" cannot target function (allowed targets: method) in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/return_type_will_change_property_error.phpt b/Zend/tests/type_declarations/variance/return_type_will_change_property_error.phpt
new file mode 100644
index 0000000000000..ebb2b49c2f8c3
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/return_type_will_change_property_error.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test that the ReturnTypeWillChange attribute cannot be used with functions
+--FILE--
+<?php
+
+class Foo
+{
+    #[ReturnTypeWillChange]
+    public int $bar;
+}
+
+?>
+--EXPECTF--
+Fatal error: Attribute "ReturnTypeWillChange" cannot target property (allowed targets: method) in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/suppressed_incompatible_return_type.phpt b/Zend/tests/type_declarations/variance/suppressed_incompatible_return_type.phpt
new file mode 100644
index 0000000000000..5da5f8d513032
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/suppressed_incompatible_return_type.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Test that the notice can be suppressed when the return type/value of the overriding method is incompatible with the tentative return type/value of the overridden method
+--FILE--
+<?php
+
+class MyDateTime extends DateTime
+{
+    /**
+     * @return DateTime|false
+     */
+    #[ReturnTypeWillChange]
+    public function modify(string $modifier) {
+        return false;
+    }
+}
+
+$date = new MyDateTime("2021-01-01 00:00:00");
+var_dump($date->modify("+1 sec"));
+?>
+--EXPECT--
+bool(false)
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 50764faeca367..133791f300ecd 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -107,66 +107,91 @@ typedef struct _zend_fcall_info_cache {
 
 #define ZEND_FE_END            { NULL, NULL, NULL, 0, 0 }
 
-#define _ZEND_ARG_INFO_FLAGS(pass_by_ref, is_variadic) \
-	(((pass_by_ref) << _ZEND_SEND_MODE_SHIFT) | ((is_variadic) ? _ZEND_IS_VARIADIC_BIT : 0))
+#define _ZEND_ARG_INFO_FLAGS(pass_by_ref, is_variadic, is_tentative) \
+	(((pass_by_ref) << _ZEND_SEND_MODE_SHIFT) | ((is_variadic) ? _ZEND_IS_VARIADIC_BIT : 0) | ((is_tentative) ? _ZEND_IS_TENTATIVE_BIT : 0))
 
 /* Arginfo structures without type information */
 #define ZEND_ARG_INFO(pass_by_ref, name) \
-	{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
+	{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL },
 #define ZEND_ARG_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, default_value) \
-	{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
+	{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value },
 #define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) \
-	{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL },
+	{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 1, 0)), NULL },
 /* Arginfo structures with simple type information */
 #define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \
-	{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
+	{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL },
 #define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \
-	{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
+	{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value },
 #define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \
-	{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL },
+	{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1, 0)), NULL },
 /* Arginfo structures with complex type information */
 #define ZEND_ARG_TYPE_MASK(pass_by_ref, name, type_mask, default_value) \
-	{ #name, ZEND_TYPE_INIT_MASK(type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
+	{ #name, ZEND_TYPE_INIT_MASK(type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value },
 #define ZEND_ARG_OBJ_TYPE_MASK(pass_by_ref, name, class_name, type_mask, default_value) \
-	{ #name, ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
+	{ #name, ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value },
 /* Arginfo structures with object type information */
 #define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) \
-	{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
+	{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL },
 #define ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, classname, allow_null, default_value) \
-	{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
+	{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), default_value },
 #define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) \
-	{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL },
+	{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1, 0)), NULL },
 /* Legacy arginfo structures */
 #define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) \
-	{ #name, ZEND_TYPE_INIT_CODE(IS_ARRAY, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
+	{ #name, ZEND_TYPE_INIT_CODE(IS_ARRAY, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL },
 #define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) \
-	{ #name, ZEND_TYPE_INIT_CODE(IS_CALLABLE, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
+	{ #name, ZEND_TYPE_INIT_CODE(IS_CALLABLE, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0, 0)), NULL },
 
-#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
+#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, return_reference, required_num_args, class_name, allow_null, is_tentative_return_type) \
 	static const zend_internal_arg_info name[] = { \
 		{ (const char*)(zend_uintptr_t)(required_num_args), \
-			ZEND_TYPE_INIT_CLASS_CONST(#class_name, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
+			ZEND_TYPE_INIT_CLASS_CONST(#class_name, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL },
+
+#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
+	ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, return_reference, required_num_args, class_name, allow_null, 0)
+
+#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
+	ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, return_reference, required_num_args, class_name, allow_null, 1)
 
 #define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(name, class_name, allow_null) \
-	ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, 0, -1, class_name, allow_null)
+	ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX2(name, 0, -1, class_name, allow_null, 0)
+
+#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX2(name, return_reference, required_num_args, type, is_tentative_return_type) \
+	static const zend_internal_arg_info name[] = { \
+		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_MASK(type | _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL },
 
 #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \
+	ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX2(name, return_reference, required_num_args, type, 0)
+
+#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \
+	ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX2(name, return_reference, required_num_args, type, 1)
+
+#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX2(name, return_reference, required_num_args, class_name, type, is_tentative_return_type) \
 	static const zend_internal_arg_info name[] = { \
-		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_MASK(type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
+		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type | _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL },
 
 #define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(name, return_reference, required_num_args, class_name, type) \
+	ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX2(name, return_reference, required_num_args, class_name, type, 0)
+
+#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(name, return_reference, required_num_args, class_name, type) \
+	ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX2(name, return_reference, required_num_args, class_name, type, 1)
+
+#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, return_reference, required_num_args, type, allow_null, is_tentative_return_type) \
 	static const zend_internal_arg_info name[] = { \
-		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
+		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CODE(type, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0, is_tentative_return_type)), NULL },
 
 #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
-	static const zend_internal_arg_info name[] = { \
-		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CODE(type, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
+	ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, return_reference, required_num_args, type, allow_null, 0)
+
+#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
+	ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, return_reference, required_num_args, type, allow_null, 1)
+
 #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, allow_null) \
-	ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, allow_null)
+	ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX2(name, 0, -1, type, allow_null, 0)
 
 #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)	\
 	static const zend_internal_arg_info name[] = { \
-		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
+		{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(return_reference, 0, 0)), NULL },
 #define ZEND_BEGIN_ARG_INFO(name, _unused)	\
 	ZEND_BEGIN_ARG_INFO_EX(name, {}, ZEND_RETURN_VALUE, -1)
 #define ZEND_END_ARG_INFO()		};
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index a09c13fd89ece..0d4a9dff5aaff 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -2201,8 +2201,8 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
 			zend_ast_export_name(str, ast->child[1], 0, indent);
 			APPEND_DEFAULT_VALUE(2);
 		case ZEND_AST_ENUM_CASE:
-			if (ast->child[2]) {
-				zend_ast_export_attributes(str, ast->child[2], indent, 1);
+			if (ast->child[3]) {
+				zend_ast_export_attributes(str, ast->child[3], indent, 1);
 			}
 			smart_str_appends(str, "case ");
 			zend_ast_export_name(str, ast->child[0], 0, indent);
@@ -2329,14 +2329,12 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr)
 		ast->child[2] = attr;
 		break;
 	case ZEND_AST_PARAM:
+	case ZEND_AST_ENUM_CASE:
 		ast->child[3] = attr;
 		break;
 	case ZEND_AST_CLASS_CONST_GROUP:
 		ast->child[1] = attr;
 		break;
-	case ZEND_AST_ENUM_CASE:
-		ast->child[2] = attr;
-		break;
 	EMPTY_SWITCH_DEFAULT_CASE()
 	}
 
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index fb6587b48cd31..0e3468ebde110 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -159,7 +159,6 @@ enum _zend_ast_kind {
 	ZEND_AST_PROP_GROUP,
 	ZEND_AST_PROP_ELEM,
 	ZEND_AST_CONST_ELEM,
-	ZEND_AST_ENUM_CASE,
 
 	// Pseudo node for initializing enums
 	ZEND_AST_CONST_ENUM_INIT,
@@ -167,6 +166,7 @@ enum _zend_ast_kind {
 	/* 4 child nodes */
 	ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
 	ZEND_AST_FOREACH,
+	ZEND_AST_ENUM_CASE,
 
 	/* 5 child nodes */
 	ZEND_AST_PARAM = 5 << ZEND_AST_NUM_CHILDREN_SHIFT,
diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c
index 2823d3bafb3ea..e04547300f75f 100644
--- a/Zend/zend_attributes.c
+++ b/Zend/zend_attributes.c
@@ -24,6 +24,7 @@
 #include "zend_smart_str.h"
 
 ZEND_API zend_class_entry *zend_ce_attribute;
+ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute;
 
 static HashTable internal_attributes;
 
@@ -67,6 +68,11 @@ ZEND_METHOD(Attribute, __construct)
 	ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags);
 }
 
+ZEND_METHOD(ReturnTypeWillChange, __construct)
+{
+	ZEND_PARSE_PARAMETERS_NONE();
+}
+
 static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
 {
 	if (attributes) {
@@ -278,6 +284,9 @@ void zend_register_attribute_ce(void)
 	zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PARAMETER"), ZEND_ATTRIBUTE_TARGET_PARAMETER);
 	zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_ALL"), ZEND_ATTRIBUTE_TARGET_ALL);
 	zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("IS_REPEATABLE"), ZEND_ATTRIBUTE_IS_REPEATABLE);
+
+	zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange();
+	zend_internal_attribute_register(zend_ce_return_type_will_change_attribute, ZEND_ATTRIBUTE_TARGET_METHOD);
 }
 
 void zend_attributes_shutdown(void)
diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php
index 26defa8d4db60..70b0c49aeb9f2 100644
--- a/Zend/zend_attributes.stub.php
+++ b/Zend/zend_attributes.stub.php
@@ -8,3 +8,8 @@ final class Attribute
 
     public function __construct(int $flags = Attribute::TARGET_ALL) {}
 }
+
+final class ReturnTypeWillChange
+{
+    public function __construct() {}
+}
diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h
index a09f9161fd61e..5f62eb8fd057d 100644
--- a/Zend/zend_attributes_arginfo.h
+++ b/Zend/zend_attributes_arginfo.h
@@ -1,12 +1,16 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0183e750e66999862a7688ecb251017110d06d1f */
+ * Stub hash: 3fd949e1b9f49666bed3081ed1e8e711acd9f49c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReturnTypeWillChange___construct, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(Attribute, __construct);
+ZEND_METHOD(ReturnTypeWillChange, __construct);
 
 
 static const zend_function_entry class_Attribute_methods[] = {
@@ -14,6 +18,12 @@ static const zend_function_entry class_Attribute_methods[] = {
 	ZEND_FE_END
 };
 
+
+static const zend_function_entry class_ReturnTypeWillChange_methods[] = {
+	ZEND_ME(ReturnTypeWillChange, __construct, arginfo_class_ReturnTypeWillChange___construct, ZEND_ACC_PUBLIC)
+	ZEND_FE_END
+};
+
 static zend_class_entry *register_class_Attribute(void)
 {
 	zend_class_entry ce, *class_entry;
@@ -30,3 +40,14 @@ static zend_class_entry *register_class_Attribute(void)
 
 	return class_entry;
 }
+
+static zend_class_entry *register_class_ReturnTypeWillChange(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "ReturnTypeWillChange", class_ReturnTypeWillChange_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+	class_entry->ce_flags |= ZEND_ACC_FINAL;
+
+	return class_entry;
+}
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 53f64b3470bc9..f426c5fcb1ad2 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -1637,33 +1637,12 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
 }
 /* }}} */
 
-void debug_print_backtrace_args(smart_str *str, zval *arg_array) /* {{{ */
-{
-	zend_string *name;
-	zval *tmp;
-	int i = 0;
-
-	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(arg_array), name, tmp) {
-		if (i++) {
-			smart_str_appends(str, ", ");
-		}
-		if (name) {
-			smart_str_append(str, name);
-			smart_str_appends(str, ": ");
-		}
-		zend_print_flat_zval_r_to_buf(str, tmp);
-	} ZEND_HASH_FOREACH_END();
-}
-/* }}} */
-
 /* {{{ */
 ZEND_FUNCTION(debug_print_backtrace)
 {
 	zend_long options = 0;
 	zend_long limit = 0;
-	zval backtrace, *frame;
-	zend_long frame_no;
-	smart_str str = {0};
+	zval backtrace;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &options, &limit) == FAILURE) {
 		RETURN_THROWS();
@@ -1671,48 +1650,11 @@ ZEND_FUNCTION(debug_print_backtrace)
 
 	zend_fetch_debug_backtrace(&backtrace, 1, options, limit);
 	ZEND_ASSERT(Z_TYPE(backtrace) == IS_ARRAY);
-	ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARR(backtrace), frame_no, frame) {
-		ZEND_ASSERT(Z_TYPE_P(frame) == IS_ARRAY);
-		zval *function = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), 1);
-		zval *class = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_CLASS), 1);
-		zval *type = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_TYPE), 1);
-		zval *file = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_FILE), 1);
-		zval *line = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_LINE), 1);
-		zval *args = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_ARGS), 1);
-
-		smart_str_append_printf(&str, "#%-2d ", (int) frame_no);
-		if (class) {
-			ZEND_ASSERT(Z_TYPE_P(class) == IS_STRING);
-			ZEND_ASSERT(type && Z_TYPE_P(type) == IS_STRING);
-			/* Cut off anonymous class names at null byte. */
-			smart_str_appends(&str, Z_STRVAL_P(class));
-			smart_str_append(&str, Z_STR_P(type));
-		}
-		smart_str_append(&str, Z_STR_P(function));
-		smart_str_appendc(&str, '(');
-		if (args) {
-			ZEND_ASSERT(Z_TYPE_P(args) == IS_ARRAY);
-			debug_print_backtrace_args(&str, args);
-		}
-		smart_str_appendc(&str, ')');
-		if (file) {
-			ZEND_ASSERT(Z_TYPE_P(file) == IS_STRING);
-			ZEND_ASSERT(line && Z_TYPE_P(line) == IS_LONG);
-			smart_str_appends(&str, " called at [");
-			smart_str_append(&str, Z_STR_P(file));
-			smart_str_appendc(&str, ':');
-			smart_str_append_long(&str, Z_LVAL_P(line));
-			smart_str_appendc(&str, ']');
-		}
-		smart_str_appendc(&str, '\n');
-	} ZEND_HASH_FOREACH_END();
-	zval_ptr_dtor(&backtrace);
 
-	smart_str_0(&str);
-	if (str.s) {
-		ZEND_WRITE(ZSTR_VAL(str.s), ZSTR_LEN(str.s));
-	}
-	smart_str_free(&str);
+	zend_string *str = zend_trace_to_string(Z_ARRVAL(backtrace), /* include_main */ false);
+	ZEND_WRITE(ZSTR_VAL(str), ZSTR_LEN(str));
+	zend_string_release(str);
+	zval_ptr_dtor(&backtrace);
 }
 
 /* }}} */
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 52d5c5d1ba059..ad8e68b9c8f40 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -28,11 +28,6 @@
 #include "zend_globals.h"
 #include "zend_closures_arginfo.h"
 
-#define ZEND_CLOSURE_PRINT_NAME "Closure object"
-
-#define ZEND_CLOSURE_PROPERTY_ERROR() \
-	zend_throw_error(NULL, "Closure object cannot have properties")
-
 typedef struct _zend_closure {
 	zend_object       std;
 	zend_function     func;
@@ -442,42 +437,6 @@ static zend_function *zend_closure_get_method(zend_object **object, zend_string
 }
 /* }}} */
 
-static ZEND_COLD zval *zend_closure_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv) /* {{{ */
-{
-	ZEND_CLOSURE_PROPERTY_ERROR();
-	return &EG(uninitialized_zval);
-}
-/* }}} */
-
-static ZEND_COLD zval *zend_closure_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot) /* {{{ */
-{
-	ZEND_CLOSURE_PROPERTY_ERROR();
-	return &EG(error_zval);
-}
-/* }}} */
-
-static ZEND_COLD zval *zend_closure_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot) /* {{{ */
-{
-	ZEND_CLOSURE_PROPERTY_ERROR();
-	return NULL;
-}
-/* }}} */
-
-static ZEND_COLD int zend_closure_has_property(zend_object *object, zend_string *member, int has_set_exists, void **cache_slot) /* {{{ */
-{
-	if (has_set_exists != ZEND_PROPERTY_EXISTS) {
-		ZEND_CLOSURE_PROPERTY_ERROR();
-	}
-	return 0;
-}
-/* }}} */
-
-static ZEND_COLD void zend_closure_unset_property(zend_object *object, zend_string *member, void **cache_slot) /* {{{ */
-{
-	ZEND_CLOSURE_PROPERTY_ERROR();
-}
-/* }}} */
-
 static void zend_closure_free_storage(zend_object *object) /* {{{ */
 {
 	zend_closure *closure = (zend_closure *)object;
@@ -645,11 +604,6 @@ void zend_register_closure_ce(void) /* {{{ */
 	closure_handlers.free_obj = zend_closure_free_storage;
 	closure_handlers.get_constructor = zend_closure_get_constructor;
 	closure_handlers.get_method = zend_closure_get_method;
-	closure_handlers.write_property = zend_closure_write_property;
-	closure_handlers.read_property = zend_closure_read_property;
-	closure_handlers.get_property_ptr_ptr = zend_closure_get_property_ptr_ptr;
-	closure_handlers.has_property = zend_closure_has_property;
-	closure_handlers.unset_property = zend_closure_unset_property;
 	closure_handlers.compare = zend_closure_compare;
 	closure_handlers.clone_obj = zend_closure_clone;
 	closure_handlers.get_debug_info = zend_closure_get_debug_info;
diff --git a/Zend/zend_closures.stub.php b/Zend/zend_closures.stub.php
index 4bd93e241fa6e..3d451e58b69b2 100644
--- a/Zend/zend_closures.stub.php
+++ b/Zend/zend_closures.stub.php
@@ -2,6 +2,7 @@
 
 /** @generate-class-entries */
 
+/** @strict-properties */
 final class Closure
 {
     private function __construct() {}
diff --git a/Zend/zend_closures_arginfo.h b/Zend/zend_closures_arginfo.h
index 56bd16ffb65b1..888e4994a08c6 100644
--- a/Zend/zend_closures_arginfo.h
+++ b/Zend/zend_closures_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 62da9b1e75331f30a0c63e82c9fd366e26b5724d */
+ * Stub hash: 7c4df531cdb30ac4206f43f0d40098666466b9a6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Closure___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -47,7 +47,7 @@ static zend_class_entry *register_class_Closure(void)
 
 	INIT_CLASS_ENTRY(ce, "Closure", class_Closure_methods);
 	class_entry = zend_register_internal_class_ex(&ce, NULL);
-	class_entry->ce_flags |= ZEND_ACC_FINAL;
+	class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES;
 
 	return class_entry;
 }
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index c41a501d0fc0d..07e8679885163 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1973,7 +1973,7 @@ void zend_verify_namespace(void) /* {{{ */
    Returns directory name component of path */
 ZEND_API size_t zend_dirname(char *path, size_t len)
 {
-	register char *end = path + len - 1;
+	char *end = path + len - 1;
 	unsigned int len_adjust = 0;
 
 #ifdef ZEND_WIN32
@@ -6466,7 +6466,6 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
 	uint32_t i;
 	zend_op_array *op_array = CG(active_op_array);
 	zend_arg_info *arg_infos;
-	zend_string *optional_param = NULL;
 
 	if (return_type_ast || fallback_return_type) {
 		/* Use op_array->arg_info[-1] for return type */
@@ -6476,7 +6475,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
 			arg_infos->type = zend_compile_typename(
 				return_type_ast, /* force_allow_null */ 0);
 			ZEND_TYPE_FULL_MASK(arg_infos->type) |= _ZEND_ARG_INFO_FLAGS(
-				(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0);
+				(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0, /* is_tentative */ 0);
 		} else {
 			arg_infos->type = (zend_type) ZEND_TYPE_INIT_CODE(fallback_return_type, 0, 0);
 		}
@@ -6489,6 +6488,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
 		arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children, 0);
 	}
 
+	/* Find last required parameter number for deprecation message. */
+	uint32_t last_required_param = (uint32_t) -1;
+	for (i = 0; i < list->children; ++i) {
+		zend_ast *param_ast = list->child[i];
+		zend_ast *default_ast_ptr = param_ast->child[2];
+		bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0;
+		if (!default_ast_ptr && !is_variadic) {
+			last_required_param = i;
+		}
+	}
+
 	for (i = 0; i < list->children; ++i) {
 		zend_ast *param_ast = list->child[i];
 		zend_ast *type_ast = param_ast->child[0];
@@ -6544,23 +6554,31 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
 			zend_const_expr_to_zval(&default_node.u.constant, default_ast_ptr);
 			CG(compiler_options) = cops;
 
-			if (!optional_param) {
+			if (last_required_param != (uint32_t) -1 && i < last_required_param) {
 				/* Ignore parameters of the form "Type $param = null".
 				 * This is the PHP 5 style way of writing "?Type $param", so allow it for now. */
 				bool is_implicit_nullable =
-					type_ast && Z_TYPE(default_node.u.constant) == IS_NULL;
+					type_ast && !(type_ast->attr & ZEND_TYPE_NULLABLE)
+					&& Z_TYPE(default_node.u.constant) == IS_NULL;
 				if (!is_implicit_nullable) {
-					optional_param = name;
+					zend_ast *required_param_ast = list->child[last_required_param];
+					zend_error(E_DEPRECATED,
+						"Optional parameter $%s declared before required parameter $%s "
+						"is implicitly treated as a required parameter",
+						ZSTR_VAL(name), ZSTR_VAL(zend_ast_get_str(required_param_ast->child[1])));
 				}
+
+				/* Regardless of whether we issue a deprecation, convert this parameter into
+				 * a required parameter without a default value. This ensures that it cannot be
+				 * used as an optional parameter even with named parameters. */
+				opcode = ZEND_RECV;
+				default_node.op_type = IS_UNUSED;
+				zval_ptr_dtor(&default_node.u.constant);
 			}
 		} else {
 			opcode = ZEND_RECV;
 			default_node.op_type = IS_UNUSED;
 			op_array->required_num_args = i + 1;
-			if (optional_param) {
-				zend_error(E_DEPRECATED, "Required parameter $%s follows optional parameter $%s",
-					ZSTR_VAL(name), ZSTR_VAL(optional_param));
-			}
 		}
 
 		arg_info = &arg_infos[i];
@@ -6606,7 +6624,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
 				zend_alloc_cache_slots(zend_type_get_num_classes(arg_info->type));
 		}
 
-		uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic)
+		uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic, /* is_tentative */ 0)
 			| (visibility ? _ZEND_IS_PROMOTED_BIT : 0);
 		ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags;
 		if (opcode == ZEND_RECV) {
@@ -7748,11 +7766,19 @@ static void zend_compile_enum_case(zend_ast *ast)
 
 	zval value_zv;
 	zend_const_expr_to_zval(&value_zv, &const_enum_init_ast);
-	zend_class_constant *c = zend_declare_class_constant_ex(enum_class, enum_case_name, &value_zv, ZEND_ACC_PUBLIC, NULL);
+
+	/* Doc comment has been appended as second last element in ZEND_AST_ENUM ast - attributes are conventionally last */
+	zend_ast *doc_comment_ast = ast->child[2];
+	zend_string *doc_comment = NULL;
+	if (doc_comment_ast) {
+		doc_comment = zend_string_copy(zend_ast_get_str(doc_comment_ast));
+	}
+
+	zend_class_constant *c = zend_declare_class_constant_ex(enum_class, enum_case_name, &value_zv, ZEND_ACC_PUBLIC, doc_comment);
 	ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
 	zend_ast_destroy(const_enum_init_ast);
 
-	zend_ast *attr_ast = ast->child[2];
+	zend_ast *attr_ast = ast->child[3];
 	if (attr_ast) {
 		zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
 	}
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 6291e4397b883..5c60a22086730 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -983,16 +983,19 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
 
 #define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1
 
-/* The send mode and is_variadic flag are stored as part of zend_type */
+/* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */
 #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT
 #define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2))
 #define _ZEND_IS_PROMOTED_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 3))
+#define _ZEND_IS_TENTATIVE_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 4))
 #define ZEND_ARG_SEND_MODE(arg_info) \
 	((ZEND_TYPE_FULL_MASK((arg_info)->type) >> _ZEND_SEND_MODE_SHIFT) & 3)
 #define ZEND_ARG_IS_VARIADIC(arg_info) \
 	((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_VARIADIC_BIT) != 0)
 #define ZEND_ARG_IS_PROMOTED(arg_info) \
 	((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_PROMOTED_BIT) != 0)
+#define ZEND_ARG_TYPE_IS_TENTATIVE(arg_info) \
+	((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_TENTATIVE_BIT) != 0)
 
 #define ZEND_DIM_IS					(1 << 0) /* isset fetch needed for null coalesce */
 #define ZEND_DIM_ALTERNATIVE_SYNTAX	(1 << 1) /* deprecated curly brace usage */
diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c
index 4cf49320a732a..457f4aa7ce18a 100644
--- a/Zend/zend_exceptions.c
+++ b/Zend/zend_exceptions.c
@@ -600,29 +600,13 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
 }
 /* }}} */
 
-/* {{{ Obtain the backtrace for the exception as a string (instead of an array) */
-ZEND_METHOD(Exception, getTraceAsString)
-{
-	zval *trace, *frame, rv;
+ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main) {
 	zend_ulong index;
-	zval *object;
-	zend_class_entry *base_ce;
-	smart_str str = {0};
+	zval *frame;
 	uint32_t num = 0;
+	smart_str str = {0};
 
-	ZEND_PARSE_PARAMETERS_NONE();
-
-	object = ZEND_THIS;
-	base_ce = i_get_exception_base(Z_OBJ_P(object));
-
-	trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
-	if (EG(exception)) {
-		RETURN_THROWS();
-	}
-
-	/* Type should be guaranteed by property type. */
-	ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY);
-	ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) {
+	ZEND_HASH_FOREACH_NUM_KEY_VAL(trace, index, frame) {
 		if (Z_TYPE_P(frame) != IS_ARRAY) {
 			zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
 			continue;
@@ -631,12 +615,33 @@ ZEND_METHOD(Exception, getTraceAsString)
 		_build_trace_string(&str, Z_ARRVAL_P(frame), num++);
 	} ZEND_HASH_FOREACH_END();
 
-	smart_str_appendc(&str, '#');
-	smart_str_append_long(&str, num);
-	smart_str_appends(&str, " {main}");
+	if (include_main) {
+		smart_str_appendc(&str, '#');
+		smart_str_append_long(&str, num);
+		smart_str_appends(&str, " {main}");
+	}
+
 	smart_str_0(&str);
+	return str.s ? str.s : ZSTR_EMPTY_ALLOC();
+}
+
+/* {{{ Obtain the backtrace for the exception as a string (instead of an array) */
+ZEND_METHOD(Exception, getTraceAsString)
+{
+
+	ZEND_PARSE_PARAMETERS_NONE();
 
-	RETURN_NEW_STR(str.s);
+	zval *object = ZEND_THIS;
+	zend_class_entry *base_ce = i_get_exception_base(Z_OBJ_P(object));
+	zval rv;
+	zval *trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
+	if (EG(exception)) {
+		RETURN_THROWS();
+	}
+
+	/* Type should be guaranteed by property type. */
+	ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY);
+	RETURN_NEW_STR(zend_trace_to_string(Z_ARRVAL_P(trace), /* include_main */ true));
 }
 /* }}} */
 
diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h
index f34d220adab85..2bfafba1359bd 100644
--- a/Zend/zend_exceptions.h
+++ b/Zend/zend_exceptions.h
@@ -68,6 +68,7 @@ extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex);
 
 /* show an exception using zend_error(severity,...), severity should be E_ERROR */
 ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity);
+ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main);
 
 ZEND_API ZEND_COLD void zend_throw_unwind_exit(void);
 ZEND_API ZEND_COLD void zend_throw_graceful_exit(void);
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 151eb4ecc59a3..18868dcad2a70 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1294,6 +1294,7 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con
 static bool zend_verify_internal_return_type(zend_function *zf, zval *ret)
 {
 	zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
+
 	if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) {
 		if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
 			zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index 1944e60606b74..a81af58b63e8a 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -2731,7 +2731,7 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, bucket_compar
 
 ZEND_API bool ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx)
 {
-	register const char *tmp = key;
+	const char *tmp = key;
 
 	const char *end = key + length;
 
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 1df7d5016df69..05ee34da2b52c 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -27,10 +27,21 @@
 #include "zend_operators.h"
 #include "zend_exceptions.h"
 #include "zend_enum.h"
+#include "zend_attributes.h"
 
 ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL;
 ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL;
 
+/* Unresolved means that class declarations that are currently not available are needed to
+ * determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated
+ * as an ERROR. */
+typedef enum {
+	INHERITANCE_UNRESOLVED = -1,
+	INHERITANCE_ERROR = 0,
+	INHERITANCE_WARNING = 1,
+	INHERITANCE_SUCCESS = 2,
+} inheritance_status;
+
 static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce);
 static void add_compatibility_obligation(
 		zend_class_entry *ce, const zend_function *child_fn, zend_class_entry *child_scope,
@@ -39,7 +50,12 @@ static void add_property_compatibility_obligation(
 		zend_class_entry *ce, const zend_property_info *child_prop,
 		const zend_property_info *parent_prop);
 
-static void zend_type_copy_ctor(zend_type *type, bool persistent) {
+static void ZEND_COLD emit_incompatible_method_error(
+		const zend_function *child, zend_class_entry *child_scope,
+		const zend_function *parent, zend_class_entry *parent_scope,
+		inheritance_status status);
+
+static void zend_type_copy_ctor(zend_type *type, zend_bool persistent) {
 	if (ZEND_TYPE_HAS_LIST(*type)) {
 		zend_type_list *old_list = ZEND_TYPE_LIST(*type);
 		size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types);
@@ -343,16 +359,6 @@ static bool zend_type_permits_self(
 	return 0;
 }
 
-/* Unresolved means that class declarations that are currently not available are needed to
- * determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated
- * as an ERROR. */
-typedef enum {
-	INHERITANCE_UNRESOLVED = -1,
-	INHERITANCE_ERROR = 0,
-	INHERITANCE_SUCCESS = 1,
-} inheritance_status;
-
-
 static void track_class_dependency(zend_class_entry *ce, zend_string *class_name)
 {
 	HashTable *ht;
@@ -455,7 +461,7 @@ static inheritance_status zend_perform_covariant_class_type_check(
 
 static inheritance_status zend_perform_covariant_type_check(
 		zend_class_entry *fe_scope, zend_type fe_type,
-		zend_class_entry *proto_scope, zend_type proto_type) /* {{{ */
+		zend_class_entry *proto_scope, zend_type proto_type, bool tentative) /* {{{ */
 {
 	ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type));
 
@@ -496,7 +502,7 @@ static inheritance_status zend_perform_covariant_type_check(
 
 		if (added_types) {
 			/* Otherwise adding new types is illegal */
-			return INHERITANCE_ERROR;
+			return tentative ? INHERITANCE_WARNING : INHERITANCE_ERROR;
 		}
 	}
 
@@ -522,7 +528,7 @@ static inheritance_status zend_perform_covariant_type_check(
 		}
 
 		if (status == INHERITANCE_ERROR) {
-			return INHERITANCE_ERROR;
+			return tentative ? INHERITANCE_WARNING : INHERITANCE_ERROR;
 		}
 		if (status != INHERITANCE_SUCCESS) {
 			all_success = 0;
@@ -570,7 +576,7 @@ static inheritance_status zend_do_perform_arg_type_hint_check(
 	/* Contravariant type check is performed as a covariant type check with swapped
 	 * argument order. */
 	return zend_perform_covariant_type_check(
-		proto_scope, proto_arg_info->type, fe_scope, fe_arg_info->type);
+		proto_scope, proto_arg_info->type, fe_scope, fe_arg_info->type, 0);
 }
 /* }}} */
 
@@ -660,18 +666,24 @@ static inheritance_status zend_do_perform_implementation_check(
 	/* Check return type compatibility, but only if the prototype already specifies
 	 * a return type. Adding a new return type is always valid. */
 	if (proto->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
-		/* Removing a return type is not valid. */
+		/* Removing a return type is not valid, unless the parent return type is tentative. */
 		if (!(fe->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
-			return INHERITANCE_ERROR;
+			if (!ZEND_ARG_TYPE_IS_TENTATIVE(&proto->common.arg_info[-1])) {
+				return INHERITANCE_ERROR;
+			}
+			if (status == INHERITANCE_SUCCESS) {
+				return INHERITANCE_WARNING;
+			}
+			return status;
 		}
 
 		local_status = zend_perform_covariant_type_check(
 			fe_scope, fe->common.arg_info[-1].type,
-			proto_scope, proto->common.arg_info[-1].type);
+			proto_scope, proto->common.arg_info[-1].type, ZEND_ARG_TYPE_IS_TENTATIVE(&proto->common.arg_info[-1]));
 
 		if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) {
-			if (UNEXPECTED(local_status == INHERITANCE_ERROR)) {
-				return INHERITANCE_ERROR;
+			if (UNEXPECTED(local_status == INHERITANCE_ERROR || local_status == INHERITANCE_WARNING)) {
+				return local_status;
 			}
 			ZEND_ASSERT(local_status == INHERITANCE_UNRESOLVED);
 			status = INHERITANCE_UNRESOLVED;
@@ -854,6 +866,18 @@ static void ZEND_COLD emit_incompatible_method_error(
 		zend_error_at(E_COMPILE_ERROR, NULL, func_lineno(child),
 			"Could not check compatibility between %s and %s, because class %s is not available",
 			ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype), ZSTR_VAL(unresolved_class));
+	} else if (status == INHERITANCE_WARNING) {
+		zend_attribute *return_type_will_change_attribute = zend_get_attribute_str(
+			child->common.attributes,
+			"returntypewillchange",
+			sizeof("returntypewillchange")-1
+		);
+
+		if (!return_type_will_change_attribute) {
+			zend_error_at(E_DEPRECATED, NULL, func_lineno(child),
+				"Declaration of %s should be compatible with %s",
+				ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype));
+		}
 	} else {
 		zend_error_at(E_COMPILE_ERROR, NULL, func_lineno(child),
 			"Declaration of %s must be compatible with %s",
@@ -874,7 +898,7 @@ static void perform_delayable_implementation_check(
 		if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
 			add_compatibility_obligation(ce, fe, fe_scope, proto, proto_scope);
 		} else {
-			ZEND_ASSERT(status == INHERITANCE_ERROR);
+			ZEND_ASSERT(status == INHERITANCE_ERROR || status == INHERITANCE_WARNING);
 			emit_incompatible_method_error(fe, fe_scope, proto, proto_scope, status);
 		}
 	}
@@ -1048,9 +1072,9 @@ inheritance_status property_types_compatible(
 
 	/* Perform a covariant type check in both directions to determined invariance. */
 	inheritance_status status1 = zend_perform_covariant_type_check(
-		child_info->ce, child_info->type, parent_info->ce, parent_info->type);
+		child_info->ce, child_info->type, parent_info->ce, parent_info->type, 0);
 	inheritance_status status2 = zend_perform_covariant_type_check(
-		parent_info->ce, parent_info->type, child_info->ce, child_info->type);
+		parent_info->ce, parent_info->type, child_info->ce, child_info->type, 0);
 	if (status1 == INHERITANCE_SUCCESS && status2 == INHERITANCE_SUCCESS) {
 		return INHERITANCE_SUCCESS;
 	}
@@ -2936,6 +2960,10 @@ zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *pa
 		orig_linking_class = CG(current_linking_class);
 		CG(current_linking_class) = is_cacheable ? ce : NULL;
 
+		if (is_cacheable) {
+			zend_begin_record_errors();
+		}
+
 		zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
 		if (parent_ce && parent_ce->num_interfaces) {
 			zend_do_inherit_interfaces(ce, parent_ce);
@@ -2948,6 +2976,7 @@ zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *pa
 		ce->ce_flags |= ZEND_ACC_LINKED;
 
 		CG(current_linking_class) = orig_linking_class;
+		EG(record_errors) = false;
 
 		if (is_cacheable) {
 			HashTable *ht = (HashTable*)ce->inheritance_cache;
diff --git a/Zend/zend_ini_scanner.l b/Zend/zend_ini_scanner.l
index 16b58ac00afc0..73853f6ec1171 100644
--- a/Zend/zend_ini_scanner.l
+++ b/Zend/zend_ini_scanner.l
@@ -305,7 +305,7 @@ zend_result zend_ini_prepare_string_for_scanning(char *str, int scanner_mode)
 /* {{{ zend_ini_escape_string() */
 static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type)
 {
-	register char *s, *t;
+	char *s, *t;
 	char *end;
 
 	zend_ini_copy_value(lval, str, len);
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index fcbdcb622261b..917a34b8c3c29 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -607,8 +607,8 @@ enum_backing_type:
 ;
 
 enum_case:
-		T_CASE identifier enum_case_expr ';'
-			{ $$ = zend_ast_create(ZEND_AST_ENUM_CASE, $2, $3, NULL); }
+		T_CASE backup_doc_comment identifier enum_case_expr ';'
+			{ $$ = zend_ast_create(ZEND_AST_ENUM_CASE, $3, $4, ($2 ? zend_ast_create_zval_from_str($2) : NULL), NULL); }
 ;
 
 enum_case_expr:
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index 02f67d02dc77b..02d79633480c4 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -905,7 +905,7 @@ ZEND_API void zend_multibyte_yyinput_again(zend_encoding_filter old_input_filter
 
 static zend_result zend_scan_escape_string(zval *zendlval, char *str, int len, char quote_type)
 {
-	register char *s, *t;
+	char *s, *t;
 	char *end;
 
 	if (len <= 1) {
@@ -2427,7 +2427,7 @@ inline_char_handler:
 
 
 <ST_IN_SCRIPTING>b?['] {
-	register char *s, *t;
+	char *s, *t;
 	char *end;
 	int bprefix = (yytext[0] != '\'') ? 1 : 0;
 
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index 8b0fed8e90a24..2df256cc52c81 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -2683,13 +2683,13 @@ ZEND_API void ZEND_FASTCALL zend_str_tolower(char *str, size_t length) /* {{{ */
 
 ZEND_API char* ZEND_FASTCALL zend_str_tolower_dup_ex(const char *source, size_t length) /* {{{ */
 {
-	register const unsigned char *p = (const unsigned char*)source;
-	register const unsigned char *end = p + length;
+	const unsigned char *p = (const unsigned char*)source;
+	const unsigned char *end = p + length;
 
 	while (p < end) {
 		if (*p != zend_tolower_ascii(*p)) {
 			char *res = (char*)emalloc(length + 1);
-			register unsigned char *r;
+			unsigned char *r;
 
 			if (p != (const unsigned char*)source) {
 				memcpy(res, source, p - (const unsigned char*)source);
@@ -3280,8 +3280,8 @@ static zend_always_inline void zend_memnstr_ex_pre(unsigned int td[], const char
 ZEND_API const char* ZEND_FASTCALL zend_memnstr_ex(const char *haystack, const char *needle, size_t needle_len, const char *end) /* {{{ */
 {
 	unsigned int td[256];
-	register size_t i;
-	register const char *p;
+	size_t i;
+	const char *p;
 
 	if (needle_len == 0 || (end - haystack) < needle_len) {
 		return NULL;
@@ -3314,8 +3314,8 @@ ZEND_API const char* ZEND_FASTCALL zend_memnstr_ex(const char *haystack, const c
 ZEND_API const char* ZEND_FASTCALL zend_memnrstr_ex(const char *haystack, const char *needle, size_t needle_len, const char *end) /* {{{ */
 {
 	unsigned int td[256];
-	register size_t i;
-	register const char *p;
+	size_t i;
+	const char *p;
 
 	if (needle_len == 0 || (end - haystack) < needle_len) {
 		return NULL;
diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c
index c444bafcdce24..6e988ad963366 100644
--- a/Zend/zend_virtual_cwd.c
+++ b/Zend/zend_virtual_cwd.c
@@ -301,7 +301,7 @@ CWD_API char *virtual_getcwd(char *buf, size_t size) /* {{{ */
 #ifdef ZEND_WIN32
 static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) /* {{{ */
 {
-	register zend_ulong h;
+	zend_ulong h;
 	size_t bucket_key_len;
 	const char *bucket_key_start = tsrm_win32_get_path_sid_key(path, path_len, &bucket_key_len);
 	const char *bucket_key = bucket_key_start;
@@ -325,7 +325,7 @@ static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) /
 #else
 static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) /* {{{ */
 {
-	register zend_ulong h;
+	zend_ulong h;
 	const char *e = path + path_len;
 
 	for (h = Z_UL(2166136261); path < e;) {
diff --git a/appveyor/test_task.bat b/appveyor/test_task.bat
index e6d88ffefc04f..87eee6de8bd79 100644
--- a/appveyor/test_task.bat
+++ b/appveyor/test_task.bat
@@ -102,6 +102,6 @@ nmake test TESTS="%OPCACHE_OPTS% -q --offline --show-diff --show-slow 1000 --set
 
 set EXIT_CODE=%errorlevel%
 
-powershell -Command "$wc = New-Object 'System.Net.WebClient'; $wc.UploadFile('https://ci.appveyor.com/api/testresults/junit/%APPVEYOR_JOB_ID%', 'c:\junit.out.xml')"
+appveyor PushArtifact %TEST_PHP_JUNIT%
 
 exit /b %EXIT_CODE%
diff --git a/build/Makefile.global b/build/Makefile.global
index 2ff838cb3318d..6941bab6ad412 100644
--- a/build/Makefile.global
+++ b/build/Makefile.global
@@ -117,6 +117,7 @@ clean:
 	find . -name .libs -a -type d|xargs rm -rf
 	rm -f libphp.la $(SAPI_CLI_PATH) $(SAPI_CGI_PATH) $(SAPI_LITESPEED_PATH) $(SAPI_FPM_PATH) $(OVERALL_TARGET) modules/* libs/*
 	rm -f ext/opcache/jit/zend_jit_x86.c
+	rm -f ext/opcache/jit/zend_jit_arm64.c
 
 distclean: clean
 	rm -f Makefile config.cache config.log config.status Makefile.objects Makefile.fragments libtool main/php_config.h main/internal_functions_cli.c main/internal_functions.c Zend/zend_dtrace_gen.h Zend/zend_dtrace_gen.h.bak Zend/zend_config.h
diff --git a/build/gen_stub.php b/build/gen_stub.php
index 084e142e34598..30d1eac28f0c9 100755
--- a/build/gen_stub.php
+++ b/build/gen_stub.php
@@ -599,16 +599,20 @@ class ReturnInfo {
     public $type;
     /** @var Type|null */
     public $phpDocType;
+    /** @var bool */
+    public $tentativeReturnType;
 
-    public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType) {
+    public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType, bool $tentativeReturnType) {
         $this->byRef = $byRef;
         $this->type = $type;
         $this->phpDocType = $phpDocType;
+        $this->tentativeReturnType = $tentativeReturnType;
     }
 
     public function equals(ReturnInfo $other): bool {
         return $this->byRef === $other->byRef
-            && Type::equals($this->type, $other->type);
+            && Type::equals($this->type, $other->type)
+            && $this->tentativeReturnType === $other->tentativeReturnType;
     }
 
     public function getMethodSynopsisType(): ?Type {
@@ -1431,6 +1435,7 @@ function parseFunctionLike(
         $isDeprecated = false;
         $verify = true;
         $docReturnType = null;
+        $tentativeReturnType = false;
         $docParamTypes = [];
 
         if ($comment) {
@@ -1452,8 +1457,10 @@ function parseFunctionLike(
                     }
                 } else if ($tag->name === 'deprecated') {
                     $isDeprecated = true;
-                }  else if ($tag->name === 'no-verify') {
+                } else if ($tag->name === 'no-verify') {
                     $verify = false;
+                } else if ($tag->name === 'tentative-return-type') {
+                    $tentativeReturnType = true;
                 } else if ($tag->name === 'return') {
                     $docReturnType = $tag->getType();
                 } else if ($tag->name === 'param') {
@@ -1530,7 +1537,8 @@ function parseFunctionLike(
         $return = new ReturnInfo(
             $func->returnsByRef(),
             $returnType ? Type::fromNode($returnType) : null,
-            $docReturnType ? Type::fromPhpDoc($docReturnType) : null
+            $docReturnType ? Type::fromPhpDoc($docReturnType) : null,
+            $tentativeReturnType
         );
 
         return new FuncInfo(
@@ -1814,18 +1822,22 @@ protected function pName_FullyQualified(Name\FullyQualified $node) {
 function funcInfoToCode(FuncInfo $funcInfo): string {
     $code = '';
     $returnType = $funcInfo->return->type;
+    $isTentativeReturnType = $funcInfo->return->tentativeReturnType;
+
     if ($returnType !== null) {
         if (null !== $simpleReturnType = $returnType->tryToSimpleType()) {
             if ($simpleReturnType->isBuiltin) {
                 $code .= sprintf(
-                    "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)\n",
+                    "%s(%s, %d, %d, %s, %d)\n",
+                    $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
                     $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
                     $funcInfo->numRequiredArgs,
                     $simpleReturnType->toTypeCode(), $returnType->isNullable()
                 );
             } else {
                 $code .= sprintf(
-                    "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(%s, %d, %d, %s, %d)\n",
+                    "%s(%s, %d, %d, %s, %d)\n",
+                    $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
                     $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
                     $funcInfo->numRequiredArgs,
                     $simpleReturnType->toEscapedName(), $returnType->isNullable()
@@ -1835,14 +1847,16 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
             $arginfoType = $returnType->toArginfoType();
             if ($arginfoType->hasClassType()) {
                 $code .= sprintf(
-                    "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(%s, %d, %d, %s, %s)\n",
+                    "%s(%s, %d, %d, %s, %s)\n",
+                    $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
                     $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
                     $funcInfo->numRequiredArgs,
                     $arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
                 );
             } else {
                 $code .= sprintf(
-                    "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s)\n",
+                    "%s(%s, %d, %d, %s)\n",
+                    $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
                     $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
                     $funcInfo->numRequiredArgs,
                     $arginfoType->toTypeMask()
diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c
index db00909d99b1a..afab1dc069ba0 100644
--- a/ext/com_dotnet/com_handlers.c
+++ b/ext/com_dotnet/com_handlers.c
@@ -336,7 +336,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name
 
 							for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) {
 								bool by_ref = (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) != 0;
-								f.arg_info[i].type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(by_ref, 0));
+								f.arg_info[i].type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(by_ref, 0, 0));
 							}
 
 							f.num_args = bindptr.lpfuncdesc->cParams;
diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php
index 63d590d9c9909..f17acf1e7ebbb 100644
--- a/ext/date/php_date.stub.php
+++ b/ext/date/php_date.stub.php
@@ -114,214 +114,211 @@ function date_sunset(
 
 function date_sun_info(int $timestamp, float $latitude, float $longitude): array {}
 
-// NB: Adding return types to methods is a BC break!
-// For now only using @return annotations here.
-
 interface DateTimeInterface
 {
-    /** @return string */
-    public function format(string $format);
+    /** @tentative-return-type */
+    public function format(string $format): string;
 
-    /** @return DateTimeZone|false */
-    public function getTimezone();
+    /** @tentative-return-type */
+    public function getTimezone(): DateTimeZone|false;
 
-    /** @return int */
-    public function getOffset();
+    /** @tentative-return-type */
+    public function getOffset(): int;
 
-    /** @return int|false */
-    public function getTimestamp();
+    /** @tentative-return-type */
+    public function getTimestamp(): int|false;
 
-    /** @return DateInterval|false */
-    public function diff(DateTimeInterface $targetObject, bool $absolute = false);
+    /** @tentative-return-type */
+    public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval;
 
-    /** @return void */
-    public function __wakeup();
+    /** @tentative-return-type */
+    public function __wakeup(): void;
 }
 
 class DateTime implements DateTimeInterface
 {
     public function __construct(string $datetime = "now", ?DateTimeZone $timezone = null) {}
 
-    /** @return void */
-    public function __wakeup() {}
+    /** @tentative-return-type */
+    public function __wakeup(): void {}
 
-    /** @return DateTime */
-    public static function __set_state(array $array) {}
+    /** @tentative-return-type */
+    public static function __set_state(array $array): DateTime {}
 
-    /** @return DateTime */
-    public static function createFromImmutable(DateTimeImmutable $object) {}
+    /** @tentative-return-type */
+    public static function createFromImmutable(DateTimeImmutable $object): DateTime {}
 
     public static function createFromInterface(DateTimeInterface $object): DateTime {}
 
     /**
-     * @return DateTime|false
+     * @tentative-return-type
      * @alias date_create_from_format
      */
-    public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) {}
+    public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false {}
 
     /**
-     * @return array|false
+     * @tentative-return-type
      * @alias date_get_last_errors
      */
-    public static function getLastErrors() {}
+    public static function getLastErrors(): array|false {}
 
     /**
-     * @return string
+     * @tentative-return-type
      * @alias date_format
      */
-    public function format(string $format) {}
+    public function format(string $format): string {}
 
     /**
-     * @return DateTime|false
+     * @tentative-return-type
      * @alias date_modify
      */
-    public function modify(string $modifier) {}
+    public function modify(string $modifier): DateTime|false {}
 
     /**
-     * @return DateTime
+     * @tentative-return-type
      * @alias date_add
      */
-    public function add(DateInterval $interval) {}
+    public function add(DateInterval $interval): DateTime {}
 
     /**
-     * @return DateTime
+     * @tentative-return-type
      * @alias date_sub
      */
-    public function sub(DateInterval $interval) {}
+    public function sub(DateInterval $interval): DateTime {}
 
     /**
-     * @return DateTimeZone|false
+     * @tentative-return-type
      * @alias date_timezone_get
      */
-    public function getTimezone() {}
+    public function getTimezone(): DateTimeZone|false {}
 
     /**
-     * @return DateTime
+     * @tentative-return-type
      * @alias date_timezone_set
      */
-    public function setTimezone(DateTimeZone $timezone) {}
+    public function setTimezone(DateTimeZone $timezone): DateTime {}
 
     /**
-     * @return int
+     * @tentative-return-type
      * @alias date_offset_get
      */
-    public function getOffset() {}
+    public function getOffset(): int {}
 
     /**
-     * @return DateTime
+     * @tentative-return-type
      * @alias date_time_set
      */
-    public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) {}
+    public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime {}
 
     /**
-     * @return DateTime
+     * @tentative-return-type
      * @alias date_date_set
      */
-    public function setDate(int $year, int $month, int $day) {}
+    public function setDate(int $year, int $month, int $day): DateTime {}
 
     /**
-     * @return DateTime
+     * @tentative-return-type
      * @alias date_isodate_set
      */
-    public function setISODate(int $year, int $week, int $dayOfWeek = 1) {}
+    public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTime {}
 
     /**
-     * @return DateTime
+     * @tentative-return-type
      * @alias date_timestamp_set
      */
-    public function setTimestamp(int $timestamp) {}
+    public function setTimestamp(int $timestamp): DateTime {}
 
     /**
-     * @return int
+     * @tentative-return-type
      * @alias date_timestamp_get
      */
-    public function getTimestamp() {}
+    public function getTimestamp(): int {}
 
     /**
-     * @return DateInterval
+     * @tentative-return-type
      * @alias date_diff
      */
-    public function diff(DateTimeInterface $targetObject, bool $absolute = false) {}
+    public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval {}
 }
 
 class DateTimeImmutable implements DateTimeInterface
 {
     public function __construct(string $datetime = "now", ?DateTimeZone $timezone = null) {}
 
-    /** @return void */
-    public function __wakeup() {}
+    /** @tentative-return-type */
+    public function __wakeup(): void {}
 
-    /** @return DateTimeImmutable */
-    public static function __set_state(array $array) {}
+    /** @tentative-return-type */
+    public static function __set_state(array $array): DateTimeImmutable {}
 
     /**
-     * @return DateTimeImmutable|false
+     * @tentative-return-type
      * @alias date_create_immutable_from_format
      */
-    public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) {}
+    public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTimeImmutable|false {}
 
     /**
-     * @return array|false
+     * @tentative-return-type
      * @alias date_get_last_errors
      */
-    public static function getLastErrors() {}
+    public static function getLastErrors(): array|false {}
 
     /**
-     * @return string
+     * @tentative-return-type
      * @alias date_format
      */
-    public function format(string $format) {}
+    public function format(string $format): string {}
 
     /**
-     * @return DateTimeZone|false
+     * @tentative-return-type
      * @alias date_timezone_get
      */
-    public function getTimezone() {}
+    public function getTimezone(): DateTimeZone|false {}
 
     /**
-     * @return int
+     * @tentative-return-type
      * @alias date_offset_get
      */
-    public function getOffset() {}
+    public function getOffset(): int {}
 
     /**
-     * @return int
+     * @tentative-return-type
      * @alias date_timestamp_get
      */
-    public function getTimestamp() {}
+    public function getTimestamp(): int {}
 
     /**
-     * @return DateInterval
+     * @tentative-return-type
      * @alias date_diff
      */
-    public function diff(DateTimeInterface $targetObject, bool $absolute = false) {}
+    public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval {}
 
-    /** @return DateTimeImmutable|false */
-    public function modify(string $modifier) {}
+    /** @tentative-return-type */
+    public function modify(string $modifier): DateTimeImmutable|false {}
 
-    /** @return DateTimeImmutable */
-    public function add(DateInterval $interval) {}
+    /** @tentative-return-type */
+    public function add(DateInterval $interval): DateTimeImmutable {}
 
-    /** @return DateTimeImmutable */
-    public function sub(DateInterval $interval) {}
+    /** @tentative-return-type */
+    public function sub(DateInterval $interval): DateTimeImmutable {}
 
-    /** @return DateTimeImmutable */
-    public function setTimezone(DateTimeZone $timezone) {}
+    /** @tentative-return-type */
+    public function setTimezone(DateTimeZone $timezone): DateTimeImmutable {}
 
-    /** @return DateTimeImmutable */
-    public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) {}
+    /** @tentative-return-type */
+    public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTimeImmutable {}
 
-    /** @return DateTimeImmutable */
-    public function setDate(int $year, int $month, int $day) {}
+    /** @tentative-return-type */
+    public function setDate(int $year, int $month, int $day): DateTimeImmutable {}
 
-    /** @return DateTimeImmutable */
-    public function setISODate(int $year, int $week, int $dayOfWeek = 1) {}
+    /** @tentative-return-type */
+    public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTimeImmutable {}
 
-    /** @return DateTimeImmutable */
-    public function setTimestamp(int $timestamp) {}
+    /** @tentative-return-type */
+    public function setTimestamp(int $timestamp): DateTimeImmutable {}
 
-    /** @return DateTimeImmutable */
-    public static function createFromMutable(DateTime $object) {}
+    /** @tentative-return-type */
+    public static function createFromMutable(DateTime $object): DateTimeImmutable {}
 
     public static function createFromInterface(DateTimeInterface $object): DateTimeImmutable {}
 }
@@ -331,46 +328,46 @@ class DateTimeZone
     public function __construct(string $timezone) {}
 
     /**
-     * @return string
+     * @tentative-return-type
      * @alias timezone_name_get
      */
-    public function getName() {}
+    public function getName(): string {}
 
     /**
-     * @return int
+     * @tentative-return-type
      * @alias timezone_offset_get
      */
-    public function getOffset(DateTimeInterface $datetime) {}
+    public function getOffset(DateTimeInterface $datetime): int {}
 
     /**
-     * @return array|false
+     * @tentative-return-type
      * @alias timezone_transitions_get
      */
-    public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX) {}
+    public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false {}
 
     /**
-     * @return array|false
+     * @tentative-return-type
      * @alias timezone_location_get
      */
-    public function getLocation() {}
+    public function getLocation(): array|false {}
 
     /**
-     * @return array
+     * @tentative-return-type
      * @alias timezone_abbreviations_list
      */
-    public static function listAbbreviations() {}
+    public static function listAbbreviations(): array {}
 
     /**
-     * @return array
+     * @tentative-return-type
      * @alias timezone_identifiers_list
      */
-    public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) {}
+    public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array {}
 
-    /** @return void */
-    public function __wakeup() {}
+    /** @tentative-return-type */
+    public function __wakeup(): void {}
 
-    /** @return DateTimeZone */
-    public static function __set_state(array $array) {}
+    /** @tentative-return-type */
+    public static function __set_state(array $array): DateTimeZone {}
 }
 
 class DateInterval
@@ -378,22 +375,22 @@ class DateInterval
     public function __construct(string $duration) {}
 
     /**
-     * @return DateInterval|false
+     * @tentative-return-type
      * @alias date_interval_create_from_date_string
      */
-    public static function createFromDateString(string $datetime) {}
+    public static function createFromDateString(string $datetime): DateInterval|false {}
 
     /**
-     * @return string
+     * @tentative-return-type
      * @alias date_interval_format
      */
-    public function format(string $format) {}
+    public function format(string $format): string {}
 
-    /** @return void */
-    public function __wakeup() {}
+    /** @tentative-return-type */
+    public function __wakeup(): void {}
 
-    /** @return DateInterval */
-    public static function __set_state(array $array) {}
+    /** @tentative-return-type */
+    public static function __set_state(array $array): DateInterval {}
 }
 
 class DatePeriod implements IteratorAggregate
@@ -406,23 +403,23 @@ class DatePeriod implements IteratorAggregate
      */
     public function __construct($start, $interval = UNKNOWN, $end = UNKNOWN, $options = UNKNOWN) {}
 
-    /** @return DateTimeInterface */
-    public function getStartDate() {}
+    /** @tentative-return-type */
+    public function getStartDate(): DateTimeInterface {}
 
-    /** @return DateTimeInterface|null */
-    public function getEndDate() {}
+    /** @tentative-return-type */
+    public function getEndDate(): ?DateTimeInterface {}
 
-    /** @return DateInterval */
-    public function getDateInterval() {}
+    /** @tentative-return-type */
+    public function getDateInterval(): DateInterval {}
 
-    /** @return int|null */
-    public function getRecurrences() {}
+    /** @tentative-return-type */
+    public function getRecurrences(): ?int {}
 
-    /** @return void */
-    public function __wakeup() {}
+    /** @tentative-return-type */
+    public function __wakeup(): void {}
 
-    /** @return DatePeriod */
-    public static function __set_state(array $array) {}
+    /** @tentative-return-type */
+    public static function __set_state(array $array): DatePeriod {}
 
     public function getIterator(): Iterator {}
 }
diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h
index 8f90dd8cbadd6..095fc486b78e2 100644
--- a/ext/date/php_date_arginfo.h
+++ b/ext/date/php_date_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 108136459e578cc699cffcb84d3335a11f8d5c9d */
+ * Stub hash: e8bc76a5db3a225746daffe29c0b8404cf971452 */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
@@ -225,36 +225,39 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_date_sun_info, 0, 3, IS_ARRAY, 0
 	ZEND_ARG_TYPE_INFO(0, longitude, IS_DOUBLE, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeInterface_format, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface_format, 0, 1, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeInterface_getTimezone, 0, 0, 0)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTimeInterface_getTimezone, 0, 0, DateTimeZone, MAY_BE_FALSE)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeInterface_getOffset arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface_getOffset, 0, 0, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeInterface_getTimestamp arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTimeInterface_getTimestamp, 0, 0, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeInterface_diff, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeInterface_diff, 0, 1, DateInterval, 0)
 	ZEND_ARG_OBJ_INFO(0, targetObject, DateTimeInterface, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, absolute, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeInterface___wakeup arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface___wakeup, 0, 0, IS_VOID, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, datetime, IS_STRING, 0, "\"now\"")
 	ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTime___wakeup arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTime___wakeup arginfo_class_DateTimeInterface___wakeup
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime___set_state, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime___set_state, 0, 1, DateTime, 0)
 	ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_createFromImmutable, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_createFromImmutable, 0, 1, DateTime, 0)
 	ZEND_ARG_OBJ_INFO(0, object, DateTimeImmutable, 0)
 ZEND_END_ARG_INFO()
 
@@ -262,21 +265,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_createFromInterfac
 	ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_createFromFormat, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTime_createFromFormat, 0, 2, DateTime, MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
 	ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTime_getLastErrors arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTime_getLastErrors, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_DateTime_format arginfo_class_DateTimeInterface_format
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_modify, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTime_modify, 0, 1, DateTime, MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, modifier, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_add, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_add, 0, 1, DateTime, 0)
 	ZEND_ARG_OBJ_INFO(0, interval, DateInterval, 0)
 ZEND_END_ARG_INFO()
 
@@ -284,76 +288,103 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_DateTime_getTimezone arginfo_class_DateTimeInterface_getTimezone
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setTimezone, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setTimezone, 0, 1, DateTime, 0)
 	ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTime_getOffset arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTime_getOffset arginfo_class_DateTimeInterface_getOffset
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setTime, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setTime, 0, 2, DateTime, 0)
 	ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, minute, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, second, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, microsecond, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setDate, 0, 0, 3)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setDate, 0, 3, DateTime, 0)
 	ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, day, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setISODate, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setISODate, 0, 2, DateTime, 0)
 	ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, week, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dayOfWeek, IS_LONG, 0, "1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime_setTimestamp, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime_setTimestamp, 0, 1, DateTime, 0)
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTime_getTimestamp arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTime_getTimestamp arginfo_class_DateTimeInterface_getOffset
 
 #define arginfo_class_DateTime_diff arginfo_class_DateTimeInterface_diff
 
 #define arginfo_class_DateTimeImmutable___construct arginfo_class_DateTime___construct
 
-#define arginfo_class_DateTimeImmutable___wakeup arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTimeImmutable___wakeup arginfo_class_DateTimeInterface___wakeup
 
-#define arginfo_class_DateTimeImmutable___set_state arginfo_class_DateTime___set_state
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable___set_state, 0, 1, DateTimeImmutable, 0)
+	ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_createFromFormat arginfo_class_DateTime_createFromFormat
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTimeImmutable_createFromFormat, 0, 2, DateTimeImmutable, MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
+	ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_getLastErrors arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTimeImmutable_getLastErrors arginfo_class_DateTime_getLastErrors
 
 #define arginfo_class_DateTimeImmutable_format arginfo_class_DateTimeInterface_format
 
 #define arginfo_class_DateTimeImmutable_getTimezone arginfo_class_DateTimeInterface_getTimezone
 
-#define arginfo_class_DateTimeImmutable_getOffset arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTimeImmutable_getOffset arginfo_class_DateTimeInterface_getOffset
 
-#define arginfo_class_DateTimeImmutable_getTimestamp arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTimeImmutable_getTimestamp arginfo_class_DateTimeInterface_getOffset
 
 #define arginfo_class_DateTimeImmutable_diff arginfo_class_DateTimeInterface_diff
 
-#define arginfo_class_DateTimeImmutable_modify arginfo_class_DateTime_modify
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateTimeImmutable_modify, 0, 1, DateTimeImmutable, MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, modifier, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_add arginfo_class_DateTime_add
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_add, 0, 1, DateTimeImmutable, 0)
+	ZEND_ARG_OBJ_INFO(0, interval, DateInterval, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_sub arginfo_class_DateTime_add
+#define arginfo_class_DateTimeImmutable_sub arginfo_class_DateTimeImmutable_add
 
-#define arginfo_class_DateTimeImmutable_setTimezone arginfo_class_DateTime_setTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setTimezone, 0, 1, DateTimeImmutable, 0)
+	ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_setTime arginfo_class_DateTime_setTime
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setTime, 0, 2, DateTimeImmutable, 0)
+	ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, minute, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, second, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, microsecond, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_setDate arginfo_class_DateTime_setDate
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setDate, 0, 3, DateTimeImmutable, 0)
+	ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, day, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_setISODate arginfo_class_DateTime_setISODate
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setISODate, 0, 2, DateTimeImmutable, 0)
+	ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, week, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dayOfWeek, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeImmutable_setTimestamp arginfo_class_DateTime_setTimestamp
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_setTimestamp, 0, 1, DateTimeImmutable, 0)
+	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeImmutable_createFromMutable, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable_createFromMutable, 0, 1, DateTimeImmutable, 0)
 	ZEND_ARG_OBJ_INFO(0, object, DateTime, 0)
 ZEND_END_ARG_INFO()
 
@@ -365,43 +396,49 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, timezone, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeZone_getName arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_getName, 0, 0, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone_getOffset, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_getOffset, 0, 1, IS_LONG, 0)
 	ZEND_ARG_OBJ_INFO(0, datetime, DateTimeInterface, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone_getTransitions, 0, 0, 0)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTimeZone_getTransitions, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampBegin, IS_LONG, 0, "PHP_INT_MIN")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "PHP_INT_MAX")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeZone_getLocation arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTimeZone_getLocation arginfo_class_DateTime_getLastErrors
 
-#define arginfo_class_DateTimeZone_listAbbreviations arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_listAbbreviations, 0, 0, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTimeZone_listIdentifiers, 0, 0, 0)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_listIdentifiers, 0, 0, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timezoneGroup, IS_LONG, 0, "DateTimeZone::ALL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, countryCode, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DateTimeZone___wakeup arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateTimeZone___wakeup arginfo_class_DateTimeInterface___wakeup
 
-#define arginfo_class_DateTimeZone___set_state arginfo_class_DateTime___set_state
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeZone___set_state, 0, 1, DateTimeZone, 0)
+	ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateInterval___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, duration, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateInterval_createFromDateString, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_DateInterval_createFromDateString, 0, 1, DateInterval, MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_DateInterval_format arginfo_class_DateTimeInterface_format
 
-#define arginfo_class_DateInterval___wakeup arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DateInterval___wakeup arginfo_class_DateTimeInterface___wakeup
 
-#define arginfo_class_DateInterval___set_state arginfo_class_DateTime___set_state
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateInterval___set_state, 0, 1, DateInterval, 0)
+	ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DatePeriod___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, start)
@@ -410,17 +447,23 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DatePeriod___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_DatePeriod_getStartDate arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getStartDate, 0, 0, DateTimeInterface, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DatePeriod_getEndDate arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getEndDate, 0, 0, DateTimeInterface, 1)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DatePeriod_getDateInterval arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getDateInterval, 0, 0, DateInterval, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DatePeriod_getRecurrences arginfo_class_DateTimeInterface_getTimezone
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DatePeriod_getRecurrences, 0, 0, IS_LONG, 1)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_DatePeriod___wakeup arginfo_class_DateTimeInterface_getTimezone
+#define arginfo_class_DatePeriod___wakeup arginfo_class_DateTimeInterface___wakeup
 
-#define arginfo_class_DatePeriod___set_state arginfo_class_DateTime___set_state
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod___set_state, 0, 1, DatePeriod, 0)
+	ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DatePeriod_getIterator, 0, 0, Iterator, 0)
 ZEND_END_ARG_INFO()
diff --git a/ext/date/tests/DateTime_extends_basic3.phpt b/ext/date/tests/DateTime_extends_basic3.phpt
index 27fc602e00506..a1e72cf1f2c32 100644
--- a/ext/date/tests/DateTime_extends_basic3.phpt
+++ b/ext/date/tests/DateTime_extends_basic3.phpt
@@ -9,7 +9,7 @@ echo "*** Testing new DateTime() : with user format() method ***\n";
 
 class DateTimeExt extends DateTime
 {
-    public function format($format = "F j, Y, g:i:s a")
+    public function format($format = "F j, Y, g:i:s a"): string
     {
         return parent::format($format);
     }
diff --git a/ext/date/tests/bug55407.phpt b/ext/date/tests/bug55407.phpt
index c4638c94b3352..f478a52129826 100644
--- a/ext/date/tests/bug55407.phpt
+++ b/ext/date/tests/bug55407.phpt
@@ -6,7 +6,7 @@ error_reporting=-1
 <?php namespace melt\core;
 
 class DateTime extends \DateTime {
-    public static function createFromFormat($format, $time, \DateTimeZone $timezone = null) {
+    public static function createFromFormat($format, $time, \DateTimeZone $timezone = null): DateTime|false {
         return new DateTime(parent::createFromFormat($format, $time, $timezone));
     }
 }
diff --git a/ext/date/tests/bug62852_var2.phpt b/ext/date/tests/bug62852_var2.phpt
index bf85147c21ff8..57fbd6e94e955 100644
--- a/ext/date/tests/bug62852_var2.phpt
+++ b/ext/date/tests/bug62852_var2.phpt
@@ -9,7 +9,7 @@ $s2 = 'O:3:"Foo":3:{s:4:"date";s:20:"10007-06-07 03:51:49";s:13:"timezone_type";
 global $foo;
 
 class Foo extends DateTime {
-    function __wakeup() {
+    function __wakeup(): void {
         global $foo;
         $foo = $this;
         parent::__wakeup();
diff --git a/ext/date/tests/bug62852_var3.phpt b/ext/date/tests/bug62852_var3.phpt
index 8092c105943cc..df3669893f257 100644
--- a/ext/date/tests/bug62852_var3.phpt
+++ b/ext/date/tests/bug62852_var3.phpt
@@ -9,7 +9,7 @@ $s2 = 'O:3:"Foo":3:{s:4:"date";s:19:"0000-00-00 00:00:00";s:13:"timezone_type";i
 global $foo;
 
 class Foo extends DateTime {
-    function __wakeup() {
+    function __wakeup(): void {
         global $foo;
         $foo = $this;
         parent::__wakeup();
diff --git a/ext/dom/node.c b/ext/dom/node.c
index 97ab61f71b3fd..67c6b768be0c3 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -603,7 +603,7 @@ int dom_node_prefix_read(dom_object *obj, zval *retval)
 
 int dom_node_prefix_write(dom_object *obj, zval *newval)
 {
-	zend_string *str;
+	zend_string *prefix_str;
 	xmlNode *nodep, *nsnode = NULL;
 	xmlNsPtr ns = NULL, curns;
 	char *strURI;
@@ -627,17 +627,17 @@ int dom_node_prefix_write(dom_object *obj, zval *newval)
 					nsnode = xmlDocGetRootElement(nodep->doc);
 				}
 			}
-			str = zval_try_get_string(newval);
-			if (UNEXPECTED(!str)) {
+			prefix_str = zval_try_get_string(newval);
+			if (UNEXPECTED(!prefix_str)) {
 				return FAILURE;
 			}
 
-			prefix = ZSTR_VAL(str);
+			prefix = ZSTR_VAL(prefix_str);
 			if (nsnode && nodep->ns != NULL && !xmlStrEqual(nodep->ns->prefix, (xmlChar *)prefix)) {
 				strURI = (char *) nodep->ns->href;
 				if (strURI == NULL ||
-					(!strcmp(prefix, "xml") && strcmp(strURI, (char *) XML_XML_NAMESPACE)) ||
-					(nodep->type == XML_ATTRIBUTE_NODE && !strcmp(prefix, "xmlns") &&
+					(zend_string_equals_literal(prefix_str, "xml") && strcmp(strURI, (char *) XML_XML_NAMESPACE)) ||
+					(nodep->type == XML_ATTRIBUTE_NODE && zend_string_equals_literal(prefix_str, "xmlns") &&
 					 strcmp(strURI, (char *) DOM_XMLNS_NAMESPACE)) ||
 					(nodep->type == XML_ATTRIBUTE_NODE && !strcmp((char *) nodep->name, "xmlns"))) {
 					ns = NULL;
@@ -656,14 +656,14 @@ int dom_node_prefix_write(dom_object *obj, zval *newval)
 				}
 
 				if (ns == NULL) {
-					zend_string_release_ex(str, 0);
+					zend_string_release_ex(prefix_str, 0);
 					php_dom_throw_error(NAMESPACE_ERR, dom_get_strict_error(obj->document));
 					return FAILURE;
 				}
 
 				xmlSetNs(nodep, ns);
 			}
-			zend_string_release_ex(str, 0);
+			zend_string_release_ex(prefix_str, 0);
 			break;
 		default:
 			break;
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index 0cdfccdb59a9b..39fea969f0385 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -57,13 +57,23 @@
 
 #include "gd_compat.h"
 
-#include <gd.h>
-#include <gd_errors.h>
-#include <gdfontt.h>  /* 1 Tiny font */
-#include <gdfonts.h>  /* 2 Small font */
-#include <gdfontmb.h> /* 3 Medium bold font */
-#include <gdfontl.h>  /* 4 Large font */
-#include <gdfontg.h>  /* 5 Giant font */
+#ifdef HAVE_GD_BUNDLED
+# include "libgd/gd.h"
+# include "libgd/gd_errors.h"
+# include "libgd/gdfontt.h"  /* 1 Tiny font */
+# include "libgd/gdfonts.h"  /* 2 Small font */
+# include "libgd/gdfontmb.h" /* 3 Medium bold font */
+# include "libgd/gdfontl.h"  /* 4 Large font */
+# include "libgd/gdfontg.h"  /* 5 Giant font */
+#else
+# include <gd.h>
+# include <gd_errors.h>
+# include <gdfontt.h>  /* 1 Tiny font */
+# include <gdfonts.h>  /* 2 Small font */
+# include <gdfontmb.h> /* 3 Medium bold font */
+# include <gdfontl.h>  /* 4 Large font */
+# include <gdfontg.h>  /* 5 Giant font */
+#endif
 
 #if defined(HAVE_GD_FREETYPE) && defined(HAVE_GD_BUNDLED)
 # include <ft2build.h>
diff --git a/ext/gd/libgd/gd_crop.c b/ext/gd/libgd/gd_crop.c
index b4bff27006889..676545c4dbc92 100644
--- a/ext/gd/libgd/gd_crop.c
+++ b/ext/gd/libgd/gd_crop.c
@@ -19,11 +19,12 @@
  *  (end code)
  **/
 
-#include <gd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
 
+#include "gd.h"
+
 static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color);
 static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold);
 
diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c
index a7cf69165caec..3fce0100e8d94 100644
--- a/ext/gd/libgd/gd_interpolation.c
+++ b/ext/gd/libgd/gd_interpolation.c
@@ -58,7 +58,7 @@
 #include <string.h>
 #include <math.h>
 
-#include <gd.h>
+#include "gd.h"
 #include "gdhelpers.h"
 
 #ifdef _MSC_VER
diff --git a/ext/gd/libgd/gd_wbmp.c b/ext/gd/libgd/gd_wbmp.c
index 13dc9e38d6186..22d1c4f4c66d5 100644
--- a/ext/gd/libgd/gd_wbmp.c
+++ b/ext/gd/libgd/gd_wbmp.c
@@ -51,13 +51,13 @@
    ----------------------------------------------------------------------------
  */
 
-#include <gd.h>
-#include <gdfonts.h>
-#include <gd_errors.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
 
+#include "gd.h"
+#include "gdfonts.h"
+#include "gd_errors.h"
 #include "wbmp.h"
 
 
diff --git a/ext/gettext/gettext.c b/ext/gettext/gettext.c
index 723a1a6d20252..05f41552c63fc 100644
--- a/ext/gettext/gettext.c
+++ b/ext/gettext/gettext.c
@@ -71,19 +71,16 @@ PHP_MINFO_FUNCTION(php_gettext)
 /* {{{ Set the textdomain to "domain". Returns the current domain */
 PHP_FUNCTION(textdomain)
 {
-	char *domain = NULL, *domain_name, *retval;
-	size_t domain_len = 0;
+	char *domain_name = NULL, *retval;
+	zend_string *domain = NULL;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &domain, &domain_len) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!", &domain) == FAILURE) {
 		RETURN_THROWS();
 	}
 
-	PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
-
-	if (domain != NULL && strcmp(domain, "") && strcmp(domain, "0")) {
-		domain_name = domain;
-	} else {
-		domain_name = NULL;
+	if (domain != NULL && ZSTR_LEN(domain) != 0 && !zend_string_equals_literal(domain, "0")) {
+		PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
+		domain_name = ZSTR_VAL(domain);
 	}
 
 	retval = textdomain(domain_name);
@@ -163,11 +160,12 @@ PHP_FUNCTION(dcgettext)
 /* {{{ Bind to the text domain domain_name, looking for translations in dir. Returns the current domain */
 PHP_FUNCTION(bindtextdomain)
 {
-	char *domain, *dir = NULL;
-	size_t domain_len, dir_len;
+	char *domain;
+	size_t domain_len;
+	zend_string *dir = NULL;
 	char *retval, dir_name[MAXPATHLEN];
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss!", &domain, &domain_len, &dir, &dir_len) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS!", &domain, &domain_len, &dir) == FAILURE) {
 		RETURN_THROWS();
 	}
 
@@ -182,8 +180,8 @@ PHP_FUNCTION(bindtextdomain)
 		RETURN_STRING(bindtextdomain(domain, NULL));
 	}
 
-	if (dir[0] != '\0' && strcmp(dir, "0")) {
-		if (!VCWD_REALPATH(dir, dir_name)) {
+	if (ZSTR_LEN(dir) != 0 && !zend_string_equals_literal(dir, "0")) {
+		if (!VCWD_REALPATH(ZSTR_VAL(dir), dir_name)) {
 			RETURN_FALSE;
 		}
 	} else if (!VCWD_GETCWD(dir_name, MAXPATHLEN)) {
diff --git a/ext/intl/dateformat/dateformat_class.c b/ext/intl/dateformat/dateformat_class.c
index e67971c35f223..0f5eb06a24024 100644
--- a/ext/intl/dateformat/dateformat_class.c
+++ b/ext/intl/dateformat/dateformat_class.c
@@ -77,6 +77,7 @@ zend_object *IntlDateFormatter_object_clone(zend_object *object)
 	zend_object *new_obj;
 
 	dfo = php_intl_dateformatter_fetch_object(object);
+	intl_error_reset(INTL_DATA_ERROR_P(dfo));
 
 	new_obj = IntlDateFormatter_ce_ptr->create_object(object->ce);
 	new_dfo = php_intl_dateformatter_fetch_object(new_obj);
diff --git a/ext/intl/formatter/formatter_class.c b/ext/intl/formatter/formatter_class.c
index e52c4bd0479d6..5241060a9fb6d 100644
--- a/ext/intl/formatter/formatter_class.c
+++ b/ext/intl/formatter/formatter_class.c
@@ -64,6 +64,8 @@ zend_object *NumberFormatter_object_clone(zend_object *object)
 	zend_object *new_obj;
 
 	nfo = php_intl_number_format_fetch_object(object);
+	intl_error_reset(INTL_DATA_ERROR_P(nfo));
+
 	new_obj = NumberFormatter_ce_ptr->create_object(object->ce);
 	new_nfo = php_intl_number_format_fetch_object(new_obj);
 	/* clone standard parts */
diff --git a/ext/intl/msgformat/msgformat_class.c b/ext/intl/msgformat/msgformat_class.c
index a3e8c9a1a9a69..3d7241b0da58e 100644
--- a/ext/intl/msgformat/msgformat_class.c
+++ b/ext/intl/msgformat/msgformat_class.c
@@ -62,6 +62,8 @@ zend_object *MessageFormatter_object_clone(zend_object *object)
 	zend_object *new_obj;
 
 	mfo = php_intl_messageformatter_fetch_object(object);
+	intl_error_reset(INTL_DATA_ERROR_P(mfo));
+
 	new_obj = MessageFormatter_ce_ptr->create_object(object->ce);
 	new_mfo = php_intl_messageformatter_fetch_object(new_obj);
 	/* clone standard parts */
diff --git a/ext/intl/tests/bug81019.phpt b/ext/intl/tests/bug81019.phpt
new file mode 100644
index 0000000000000..6703dae1bddc1
--- /dev/null
+++ b/ext/intl/tests/bug81019.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #81019: Unable to clone NumberFormatter after failed parse()
+--FILE--
+<?php
+
+$fmt = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
+$fmt->parse('abc');
+$fmt2 = clone $fmt;
+
+$datefmt = new IntlDateFormatter('en_US', IntlDateFormatter::FULL, IntlDateFormatter::FULL);
+$datefmt->parse('abc');
+$datefmt2 = clone $datefmt;
+
+$msgfmt = new MessageFormatter('en_US', '{0,number,integer}');
+$msgfmt->parse('abc');
+$msgfmt2 = clone $msgfmt;
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/ext/intl/tests/dateformat_create_default.phpt b/ext/intl/tests/dateformat_create_default.phpt
index cc130718f5489..eddac36f5ea39 100644
--- a/ext/intl/tests/dateformat_create_default.phpt
+++ b/ext/intl/tests/dateformat_create_default.phpt
@@ -2,6 +2,8 @@
 IntlDateFormatter::create() with default date and time types
 --EXTENSIONS--
 intl
+--INI--
+date.timezone=UTC
 --FILE--
 <?php
 
diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c
index 3da816450491e..ce7c217cd2e60 100644
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@ -357,7 +357,7 @@ static int php_mb_parse_encoding_array(HashTable *target_hash, const mbfl_encodi
 			return FAILURE;
 		}
 
-		if (strcasecmp(ZSTR_VAL(encoding_str), "auto") == 0) {
+		if (zend_string_equals_literal_ci(encoding_str, "auto")) {
 			if (!included_auto) {
 				const enum mbfl_no_encoding *src = MBSTRG(default_detect_order_list);
 				const size_t identify_list_size = MBSTRG(default_detect_order_list_size);
@@ -4232,7 +4232,7 @@ MBSTRING_API size_t php_mb_mbchar_bytes(const char *s)
 /* {{{ MBSTRING_API char *php_mb_safe_strrchr_ex() */
 MBSTRING_API char *php_mb_safe_strrchr_ex(const char *s, unsigned int c, size_t nbytes, const mbfl_encoding *enc)
 {
-	register const char *p = s;
+	const char *p = s;
 	char *last=NULL;
 
 	if (nbytes == (size_t)-1) {
@@ -4252,8 +4252,8 @@ MBSTRING_API char *php_mb_safe_strrchr_ex(const char *s, unsigned int c, size_t
 			++p;
 		}
 	} else {
-		register size_t bcnt = nbytes;
-		register size_t nbytes_char;
+		size_t bcnt = nbytes;
+		size_t nbytes_char;
 		while (bcnt > 0) {
 			if ((unsigned char)*p == (unsigned char)c) {
 				last = (char *)p;
diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c
index 7ed90a3cfb585..786ca60906e4c 100644
--- a/ext/mysqli/mysqli_api.c
+++ b/ext/mysqli/mysqli_api.c
@@ -79,7 +79,7 @@ mysqli_escape_string_for_tx_name_in_comment(const char * const name)
 		*p_copy++ = '/';
 		*p_copy++ = '*';
 		while (1) {
-			register char v = *p_orig;
+			char v = *p_orig;
 			if (v == 0) {
 				break;
 			}
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index 1f4e5a63f4dc3..14b7206c30b73 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -91,7 +91,7 @@ static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_L
 zend_ulong
 php_mysqlnd_net_field_length(const zend_uchar **packet)
 {
-	register const zend_uchar *p= (const zend_uchar *)*packet;
+	const zend_uchar *p= (const zend_uchar *)*packet;
 
 	if (*p < 251) {
 		(*packet)++;
@@ -121,7 +121,7 @@ php_mysqlnd_net_field_length(const zend_uchar **packet)
 uint64_t
 php_mysqlnd_net_field_length_ll(const zend_uchar **packet)
 {
-	register const zend_uchar *p = (zend_uchar *)*packet;
+	const zend_uchar *p = (zend_uchar *)*packet;
 
 	if (*p < 251) {
 		(*packet)++;
@@ -639,7 +639,7 @@ size_t php_mysqlnd_auth_write(MYSQLND_CONN_DATA * conn, void * _packet)
 static enum_func_status
 php_mysqlnd_auth_response_read(MYSQLND_CONN_DATA * conn, void * _packet)
 {
-	register MYSQLND_PACKET_AUTH_RESPONSE * packet= (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
+	MYSQLND_PACKET_AUTH_RESPONSE * packet= (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
 	MYSQLND_ERROR_INFO * error_info = conn->error_info;
 	MYSQLND_PFC * pfc = conn->protocol_frame_codec;
 	MYSQLND_VIO * vio = conn->vio;
@@ -802,7 +802,7 @@ php_mysqlnd_change_auth_response_write(MYSQLND_CONN_DATA * conn, void * _packet)
 static enum_func_status
 php_mysqlnd_ok_read(MYSQLND_CONN_DATA * conn, void * _packet)
 {
-	register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
+	MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
 	MYSQLND_ERROR_INFO * error_info = conn->error_info;
 	MYSQLND_PFC * pfc = conn->protocol_frame_codec;
 	MYSQLND_VIO * vio = conn->vio;
diff --git a/ext/oci8/oci8.stub.php b/ext/oci8/oci8.stub.php
index b7d47d5dc9597..caaa151f747b0 100644
--- a/ext/oci8/oci8.stub.php
+++ b/ext/oci8/oci8.stub.php
@@ -22,8 +22,11 @@ function oci_bind_by_name($statement, string $param, mixed &$var, int $max_lengt
  */
 function ocibindbyname($statement, string $param, mixed &$var, int $max_length = -1, int $type = 0): bool {}
 
-/** @param resource $statement */
-function oci_bind_array_by_name($statement, string $param, mixed &$var, int $max_array_length, int $max_item_length = -1, int $type = SQLT_AFC): bool {}
+/**
+ * @param resource $statement
+ * @param array $var
+ */
+function oci_bind_array_by_name($statement, string $param, &$var, int $max_array_length, int $max_item_length = -1, int $type = SQLT_AFC): bool {}
 
 function oci_free_descriptor(OCILob $lob): bool {}
 
@@ -247,7 +250,7 @@ function oci_fetch_all($statement, &$output, int $offset = 0, int $limit = -1, i
 function ocifetchstatement($statement, &$output, int $offset = 0, int $limit = -1, int $flags = 0): int {}
 
 /** @param resource $statement */
-function oci_fetch_object($statement, int $mode = PHP_OCI_ASSOC | PHP_OCI_RETURN_NULLS): stdClass|null|false {}
+function oci_fetch_object($statement, int $mode = PHP_OCI_ASSOC | PHP_OCI_RETURN_NULLS): stdClass|false {}
 
 /** @param resource $statement */
 function oci_fetch_row($statement): array|false {}
diff --git a/ext/oci8/oci8_arginfo.h b/ext/oci8/oci8_arginfo.h
index 4fc33e377cc68..898a9e649ad72 100644
--- a/ext/oci8/oci8_arginfo.h
+++ b/ext/oci8/oci8_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: f96a1c7a278551bf334eab82a69710c3418beebf */
+ * Stub hash: dc95519a7182bfc92aa84802c0086ae9f49579a8 */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_define_by_name, 0, 3, _IS_BOOL, 0)
 	ZEND_ARG_INFO(0, statement)
@@ -23,7 +23,7 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_bind_array_by_name, 0, 4, _IS_BOOL, 0)
 	ZEND_ARG_INFO(0, statement)
 	ZEND_ARG_TYPE_INFO(0, param, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(1, var, IS_MIXED, 0)
+	ZEND_ARG_INFO(1, var)
 	ZEND_ARG_TYPE_INFO(0, max_array_length, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, max_item_length, IS_LONG, 0, "-1")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, "SQLT_AFC")
@@ -221,7 +221,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_ocifetchstatement arginfo_oci_fetch_all
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_oci_fetch_object, 0, 1, stdClass, MAY_BE_NULL|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_oci_fetch_object, 0, 1, stdClass, MAY_BE_FALSE)
 	ZEND_ARG_INFO(0, statement)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "PHP_OCI_ASSOC | PHP_OCI_RETURN_NULLS")
 ZEND_END_ARG_INFO()
diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4
index 633618e885498..f49480117ba2e 100644
--- a/ext/opcache/config.m4
+++ b/ext/opcache/config.m4
@@ -29,7 +29,7 @@ if test "$PHP_OPCACHE" != "no"; then
 
   if test "$PHP_OPCACHE_JIT" = "yes"; then
     case $host_cpu in
-      i[[34567]]86*|x86*)
+      i[[34567]]86*|x86*|aarch64)
         ;;
       *)
         AC_MSG_WARN([JIT not supported by host architecture])
@@ -77,6 +77,7 @@ if test "$PHP_OPCACHE" != "no"; then
     fi
 
     PHP_SUBST(DASM_FLAGS)
+    PHP_SUBST(DASM_ARCH)
 
     AC_MSG_CHECKING(for opagent in default path)
     for i in /usr/local /usr; do
diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32
index a7f292ee7625f..764a2edaab146 100644
--- a/ext/opcache/config.w32
+++ b/ext/opcache/config.w32
@@ -25,6 +25,7 @@ if (PHP_OPCACHE != "no") {
 				dasm_flags += " -D ZTS=1";
 			}
 			DEFINE("DASM_FLAGS", dasm_flags);
+			DEFINE("DASM_ARCH", "x86");
 
 			AC_DEFINE('HAVE_JIT', 1, 'Define to enable JIT');
 			/* XXX read this dynamically */
diff --git a/ext/opcache/jit/Makefile.frag b/ext/opcache/jit/Makefile.frag
index d44e06a3ad91b..98c5cdaea2494 100644
--- a/ext/opcache/jit/Makefile.frag
+++ b/ext/opcache/jit/Makefile.frag
@@ -2,11 +2,11 @@
 $(builddir)/minilua: $(srcdir)/jit/dynasm/minilua.c
 	$(BUILD_CC) $(srcdir)/jit/dynasm/minilua.c -lm -o $@
 
-$(builddir)/jit/zend_jit_x86.c: $(srcdir)/jit/zend_jit_x86.dasc $(srcdir)/jit/dynasm/*.lua $(builddir)/minilua
-	$(builddir)/minilua $(srcdir)/jit/dynasm/dynasm.lua  $(DASM_FLAGS) -o $@ $(srcdir)/jit/zend_jit_x86.dasc
+$(builddir)/jit/zend_jit_$(DASM_ARCH).c: $(srcdir)/jit/zend_jit_$(DASM_ARCH).dasc $(srcdir)/jit/dynasm/*.lua $(builddir)/minilua
+	$(builddir)/minilua $(srcdir)/jit/dynasm/dynasm.lua  $(DASM_FLAGS) -o $@ $(srcdir)/jit/zend_jit_$(DASM_ARCH).dasc
 
 $(builddir)/jit/zend_jit.lo: \
-	$(builddir)/jit/zend_jit_x86.c \
+	$(builddir)/jit/zend_jit_$(DASM_ARCH).c \
 	$(srcdir)/jit/zend_jit_helpers.c \
 	$(srcdir)/jit/zend_jit_disasm.c \
 	$(srcdir)/jit/zend_jit_gdb.c \
diff --git a/ext/opcache/jit/dynasm/dasm_arm64.h b/ext/opcache/jit/dynasm/dasm_arm64.h
index 8d1d9a9654247..909b51f808a80 100644
--- a/ext/opcache/jit/dynasm/dasm_arm64.h
+++ b/ext/opcache/jit/dynasm/dasm_arm64.h
@@ -404,6 +404,15 @@ int dasm_link(Dst_DECL, size_t *szp)
   return DASM_S_OK;
 }
 
+#ifdef DASM_ADD_VENEER
+#define CK_REL(x, o) \
+  do { if (!(x) && !(n = DASM_ADD_VENEER(D, buffer, ins, b, cp, o))) \
+    return DASM_S_RANGE_REL|(p-D->actionlist-1); \
+  } while (0)
+#else
+#define CK_REL(x, o) CK(x, RANGE_REL)
+#endif
+
 #ifdef DASM_CHECKS
 #define CK(x, st) \
   do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
@@ -444,7 +453,7 @@ int dasm_encode(Dst_DECL, void *buffer)
 	  if (n < 0) {
 	    ptrdiff_t na = (ptrdiff_t)D->globals[-n] - (ptrdiff_t)cp + 4;
 	    n = (int)na;
-	    CK((ptrdiff_t)n == na, RANGE_REL);
+	    CK_REL((ptrdiff_t)n == na, na);
 	    goto patchrel;
 	  }
 	  /* fallthrough */
@@ -453,18 +462,18 @@ int dasm_encode(Dst_DECL, void *buffer)
 	  n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) + 4;
 	patchrel:
 	  if (!(ins & 0xf800)) {  /* B, BL */
-	    CK((n & 3) == 0 && ((n+0x08000000) >> 28) == 0, RANGE_REL);
+	    CK_REL((n & 3) == 0 && ((n+0x08000000) >> 28) == 0, n);
 	    cp[-1] |= ((n >> 2) & 0x03ffffff);
 	  } else if ((ins & 0x800)) {  /* B.cond, CBZ, CBNZ, LDR* literal */
-	    CK((n & 3) == 0 && ((n+0x00100000) >> 21) == 0, RANGE_REL);
+	    CK_REL((n & 3) == 0 && ((n+0x00100000) >> 21) == 0, n);
 	    cp[-1] |= ((n << 3) & 0x00ffffe0);
 	  } else if ((ins & 0x3000) == 0x2000) {  /* ADR */
-	    CK(((n+0x00100000) >> 21) == 0, RANGE_REL);
+	    CK_REL(((n+0x00100000) >> 21) == 0, n);
 	    cp[-1] |= ((n << 3) & 0x00ffffe0) | ((n & 3) << 29);
 	  } else if ((ins & 0x3000) == 0x3000) {  /* ADRP */
 	    cp[-1] |= ((n >> 9) & 0x00ffffe0) | (((n >> 12) & 3) << 29);
 	  } else if ((ins & 0x1000)) {  /* TBZ, TBNZ */
-	    CK((n & 3) == 0 && ((n+0x00008000) >> 16) == 0, RANGE_REL);
+	    CK_REL((n & 3) == 0 && ((n+0x00008000) >> 16) == 0, n);
 	    cp[-1] |= ((n << 3) & 0x0007ffe0);
 	  } else if ((ins & 0x8000)) {  /* absolute */
 	    cp[0] = (unsigned int)((ptrdiff_t)cp - 4 + n);
@@ -475,7 +484,7 @@ int dasm_encode(Dst_DECL, void *buffer)
 	case DASM_REL_A: {
 	  ptrdiff_t na = (((ptrdiff_t)(*b++) << 32) | (unsigned int)n) - (ptrdiff_t)cp + 4;
 	  n = (int)na;
-	  CK((ptrdiff_t)n == na, RANGE_REL);
+	  CK_REL((ptrdiff_t)n == na, na);
 	  goto patchrel;
 	}
 	case DASM_LABEL_LG:
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index 0cb9fc946df5c..8cd14fc987bbc 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -39,7 +39,14 @@
 #include "Optimizer/zend_call_graph.h"
 #include "Optimizer/zend_dump.h"
 
+#if defined(__x86_64__) || defined(i386) || defined(ZEND_WIN32)
 #include "jit/zend_jit_x86.h"
+#elif defined (__aarch64__)
+#include "jit/zend_jit_arm64.h"
+#else
+#error "JIT not supported on this platform"
+#endif
+
 #include "jit/zend_jit_internal.h"
 
 #ifdef ZTS
@@ -188,11 +195,6 @@ static bool zend_is_commutative(zend_uchar opcode)
 		opcode == ZEND_BW_XOR;
 }
 
-static bool zend_long_is_power_of_two(zend_long x)
-{
-	return (x > 0) && !(x & (x - 1));
-}
-
 #define OP_RANGE(ssa_op, opN) \
 	(((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \
 	  ssa->var_info && \
@@ -204,19 +206,30 @@ static bool zend_long_is_power_of_two(zend_long x)
 #define OP2_RANGE()      OP_RANGE(ssa_op, op2)
 #define OP1_DATA_RANGE() OP_RANGE(ssa_op + 1, op1)
 
+#if defined(__x86_64__) || defined(i386) || defined(ZEND_WIN32)
 #include "dynasm/dasm_x86.h"
+#elif defined(__aarch64__)
+static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int *b, uint32_t *cp, ptrdiff_t offset);
+#define DASM_ADD_VENEER zend_jit_add_veneer
+#include "dynasm/dasm_arm64.h"
+#endif
+
 #include "jit/zend_jit_helpers.c"
 #include "jit/zend_jit_disasm.c"
 #ifndef _WIN32
-#include "jit/zend_jit_gdb.c"
-#include "jit/zend_jit_perf_dump.c"
+# include "jit/zend_jit_gdb.c"
+# include "jit/zend_jit_perf_dump.c"
 #endif
 #ifdef HAVE_OPROFILE
 # include "jit/zend_jit_oprofile.c"
 #endif
-#include "jit/zend_jit_vtune.c"
 
+#if defined(__x86_64__) || defined(i386) || defined(ZEND_WIN32)
+#include "jit/zend_jit_vtune.c"
 #include "jit/zend_jit_x86.c"
+#elif defined(__aarch64__)
+#include "jit/zend_jit_arm64.c"
+#endif
 
 #if _WIN32
 # include <Windows.h>
@@ -298,15 +311,32 @@ static void handle_dasm_error(int ret) {
 		case DASM_S_RANGE_PC:
 			fprintf(stderr, "DASM_S_RANGE_PC %d\n", ret & 0xffffffu);
 			break;
+#ifdef DASM_S_RANGE_VREG
 		case DASM_S_RANGE_VREG:
 			fprintf(stderr, "DASM_S_RANGE_VREG\n");
 			break;
+#endif
+#ifdef DASM_S_UNDEF_L
 		case DASM_S_UNDEF_L:
 			fprintf(stderr, "DASM_S_UNDEF_L\n");
 			break;
+#endif
+#ifdef DASM_S_UNDEF_LG
+		case DASM_S_UNDEF_LG:
+			fprintf(stderr, "DASM_S_UNDEF_LG\n");
+			break;
+#endif
+#ifdef DASM_S_RANGE_REL
+		case DASM_S_RANGE_REL:
+			fprintf(stderr, "DASM_S_RANGE_REL\n");
+			break;
+#endif
 		case DASM_S_UNDEF_PC:
 			fprintf(stderr, "DASM_S_UNDEF_PC\n");
 			break;
+		default:
+			fprintf(stderr, "DASM_S_%0x\n", ret & 0xff000000u);
+			break;
 	}
 	ZEND_UNREACHABLE();
 }
@@ -380,6 +410,10 @@ static void *dasm_link_and_encode(dasm_State             **dasm_state,
 		return NULL;
 	}
 
+#ifdef __aarch64__
+	dasm_venners_size = 0;
+#endif
+
 	ret = dasm_encode(dasm_state, *dasm_ptr);
 	if (ret != DASM_S_OK) {
 #if ZEND_DEBUG
@@ -388,9 +422,16 @@ static void *dasm_link_and_encode(dasm_State             **dasm_state,
 		return NULL;
 	}
 
+#ifdef __aarch64__
+	size += dasm_venners_size;
+#endif
+
 	entry = *dasm_ptr;
 	*dasm_ptr = (void*)((char*)*dasm_ptr + ZEND_MM_ALIGNED_SIZE_EX(size, DASM_ALIGNMENT));
 
+	/* flush the hardware I-cache */
+	JIT_CACHE_FLUSH(entry, entry + size);
+
 	if (trace_num) {
 		zend_jit_trace_add_code(entry, size);
 	}
@@ -4365,8 +4406,14 @@ ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, bool reattached)
 		return FAILURE;
 	}
 
-	/* save JIT buffer pos */
 	zend_jit_unprotect();
+#ifdef __aarch64__
+	/* reserve space for global labels veneers */
+	dasm_labels_veneers = *dasm_ptr;
+	*dasm_ptr = (void**)*dasm_ptr + zend_lb_MAX;
+	memset(dasm_labels_veneers, 0, sizeof(void*) * zend_lb_MAX);
+#endif
+	/* save JIT buffer pos */
 	dasm_ptr[1] = dasm_ptr[0];
 	zend_jit_protect();
 
@@ -4376,7 +4423,7 @@ ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, bool reattached)
 ZEND_EXT_API void zend_jit_shutdown(void)
 {
 	if (JIT_G(debug) & ZEND_JIT_DEBUG_SIZE) {
-		fprintf(stderr, "\nJIT memory usage: %td\n", (char*)*dasm_ptr - (char*)dasm_buf);
+		fprintf(stderr, "\nJIT memory usage: %td\n", (ptrdiff_t)((char*)*dasm_ptr - (char*)dasm_buf));
 	}
 
 #ifdef HAVE_OPROFILE
diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h
index 6985ff141ea7c..753dec20ff732 100644
--- a/ext/opcache/jit/zend_jit.h
+++ b/ext/opcache/jit/zend_jit.h
@@ -53,6 +53,7 @@
 
 #define ZEND_JIT_DEBUG_GDB       (1<<8)
 #define ZEND_JIT_DEBUG_SIZE      (1<<9)
+#define ZEND_JIT_DEBUG_ASM_ADDR  (1<<10)
 
 #define ZEND_JIT_DEBUG_TRACE_START     (1<<12)
 #define ZEND_JIT_DEBUG_TRACE_STOP      (1<<13)
diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc
new file mode 100644
index 0000000000000..d1cd697ec55ee
--- /dev/null
+++ b/ext/opcache/jit/zend_jit_arm64.dasc
@@ -0,0 +1,15160 @@
+/*
+ *  +----------------------------------------------------------------------+
+ *  | Zend JIT                                                             |
+ *  +----------------------------------------------------------------------+
+ *  | Copyright (c) The PHP Group                                          |
+ *  +----------------------------------------------------------------------+
+ *  | This source file is subject to version 3.01 of the PHP license,      |
+ *  | that is bundled with this package in the file LICENSE, and is        |
+ *  | available through the world-wide-web at the following url:           |
+ *  | http://www.php.net/license/3_01.txt                                  |
+ *  | If you did not receive a copy of the PHP license and are unable to   |
+ *  | obtain it through the world-wide-web, please send a note to          |
+ *  | license@php.net so we can mail you a copy immediately.               |
+ *  +----------------------------------------------------------------------+
+ *  | Authors: Dmitry Stogov <dmitry@php.net>                              |
+ *  |          Xinchen Hui <laruence@php.net>                              |
+ *  |          Hao Sun <hao.sun@arm.com>                                   |
+ *  +----------------------------------------------------------------------+
+ */
+
+|.arch arm64
+
+|.define FP,      x27
+|.define IP,      x28
+|.define IPl,     w28
+|.define RX,      x28         // the same as VM IP reused as a general purpose reg
+|.define LR,      x30
+|.define CARG1,   x0
+|.define CARG2,   x1
+|.define CARG3,   x2
+|.define CARG4,   x3
+|.define CARG5,   x4
+|.define CARG6,   x5
+|.define CARG1w,  w0
+|.define CARG2w,  w1
+|.define CARG3w,  w2
+|.define CARG4w,  w3
+|.define CARG5w,  w4
+|.define CARG6w,  w5
+|.define RETVALx, x0
+|.define RETVALw, w0
+|.define FCARG1x, x0
+|.define FCARG1w, w0
+|.define FCARG2x, x1
+|.define FCARG2w, w1
+|.define SPAD,    0x20        // padding for CPU stack alignment
+|.define NR_SPAD, 0x30        // padding for CPU stack alignment
+|.define T3,      [sp, #0x28] // Used to store old value of IP (CALL VM only)
+|.define T2,      [sp, #0x20] // Used to store old value of FP (CALL VM only)
+|.define T1,      [sp, #0x10]
+
+// We use REG0/1/2 and FPR0/1 to replace r0/1/2 and xmm0/1 in the x86 implementation.
+// Scratch registers
+|.define REG0,    x8
+|.define REG0w,   w8
+|.define REG1,    x9
+|.define REG1w,   w9
+|.define REG2,    x10
+|.define REG2w,   w10
+|.define FPR0,    v0
+|.define FPR1,    v1
+|.define FPR0d,   d0
+|.define FPR1d,   d1
+
+|.define ZREG_REG0,   ZREG_X8
+|.define ZREG_REG1,   ZREG_X9
+|.define ZREG_REG2,   ZREG_X10
+|.define ZREG_FPR0,   ZREG_V0
+|.define ZREG_FPR1,   ZREG_V1
+
+// Temporaries, not preserved across calls
+|.define TMP1,    x15
+|.define TMP1w,   w15
+|.define TMP2,    x16
+|.define TMP2w,   w16
+|.define TMP3,    x17 // TODO: remember about hard-coded: mrs TMP3, tpidr_el0
+|.define TMP3w,   w17
+|.define FPTMP,   v16
+|.define FPTMPd,  d16
+
+|.define ZREG_TMP1,   ZREG_X15
+|.define ZREG_TMP2,   ZREG_X16
+|.define ZREG_TMP3,   ZREG_X17
+|.define ZREG_FPTMP,  ZREG_V16
+
+|.define HYBRID_SPAD, #32     // padding for stack alignment
+
+#define TMP_ZVAL_OFFSET 16
+#define DASM_ALIGNMENT  16
+
+/* Encoding of immediate. TODO: shift mode may be supported in the near future. */
+#define MAX_IMM12       0xfff          // maximum value for imm12
+#define MAX_IMM16       0xffff         // maximum value for imm16
+#define CMP_IMM         MAX_IMM12      // cmp insn
+#define MOVZ_IMM        MAX_IMM16      // movz insn
+#define ADD_SUB_IMM     MAX_IMM12      // add/sub/adds/subs insn
+#define LDR_STR_PIMM64  (MAX_IMM12*8)  // ldr/str insn for 64-bit register: pimm is imm12 * 8
+#define LDR_STR_PIMM32  (MAX_IMM12*4)  // ldr/str insn for 32-bit register: pimm is imm12 * 4
+#define LDRB_STRB_PIMM  MAX_IMM12      // ldrb/strb insn
+
+#define B_IMM26         (1<<27)        // signed imm26 * 4
+
+static bool arm64_may_use_b(const void *addr)
+{
+	if (addr >= dasm_buf && addr < dasm_end) {
+		return (((char*)dasm_end - (char*)dasm_buf) < B_IMM26);
+	} else if (addr >= dasm_end) {
+		return (((char*)addr - (char*)dasm_buf) < B_IMM26);
+	} else if (addr < dasm_buf) {
+		return (((char*)dasm_end - (char*)addr) < B_IMM26);
+	}
+	return 0;
+}
+
+#include "Zend/zend_cpuinfo.h"
+
+#ifdef HAVE_VALGRIND
+# include <valgrind/valgrind.h>
+#endif
+
+/* The generated code may contain tautological comparisons, ignore them. */
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wtautological-compare"
+# pragma clang diagnostic ignored "-Wstring-compare"
+#endif
+
+const char* zend_reg_name[] = {
+	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+	"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
+	"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+	"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
+	"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
+	"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
+};
+
+#ifdef HAVE_GCC_GLOBAL_REGS
+# define GCC_GLOBAL_REGS 1
+#else
+# define GCC_GLOBAL_REGS 0
+#endif
+
+# define ZREG_FCARG1x ZREG_X0
+# define ZREG_FCARG2x ZREG_X1
+
+#if ZTS
+static size_t tsrm_ls_cache_tcb_offset = 0;
+#endif
+
+/* By default avoid JITing inline handlers if it does not seem profitable due to lack of
+ * type information. Disabling this option allows testing some JIT handlers in the
+ * presence of try/catch blocks, which prevent SSA construction. */
+#ifndef PROFITABILITY_CHECKS
+# define PROFITABILITY_CHECKS 1
+#endif
+
+|.type EX, zend_execute_data, FP
+|.type OP, zend_op
+|.type ZVAL, zval
+
+|.actionlist dasm_actions
+
+|.globals zend_lb
+static void* dasm_labels[zend_lb_MAX];
+
+|.section code, cold_code, jmp_table
+
+#define IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1)))
+
+#define BP_JIT_IS 6
+
+/* helper: determine whether an immediate value can be encoded as the immediate operand of logical instructions */
+static int logical_immediate_p (uint64_t value, uint32_t reg_size)
+{
+	/* fast path: power of two */
+	if (value > 0 && !(value & (value - 1))) {
+		return 1;
+	}
+
+	// TODO: slow path
+	return 0;
+}
+
+/* Not Implemented Yet */
+|.macro NIY
+||	//ZEND_ASSERT(0);
+|	brk #0
+|.endmacro
+
+|.macro NIY_STUB
+||	//ZEND_ASSERT(0);
+|	brk #0
+|.endmacro
+
+|.macro ADD_HYBRID_SPAD
+||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
+|	add sp, sp, HYBRID_SPAD
+||#endif
+|.endmacro
+
+|.macro SUB_HYBRID_SPAD
+||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
+|	sub sp, sp, HYBRID_SPAD
+||#endif
+|.endmacro
+
+|.macro LOAD_ADDR, reg, addr
+|	// 48-bit virtual address
+||	if (((uintptr_t)(addr)) == 0) {
+|		mov reg, xzr
+||	} else if (((uintptr_t)(addr)) <= MOVZ_IMM) {
+| 		movz reg, #((uint64_t)(addr))
+||	} else if ((uintptr_t)(addr) & 0xffff) {
+|		movz reg, #((uintptr_t)(addr) & 0xffff)
+||		if (((uintptr_t)(addr) >> 16) & 0xffff) {
+|			movk reg, #(((uintptr_t)(addr) >> 16) & 0xffff), lsl #16
+||		}
+||		if (((uintptr_t)(addr) >> 32) & 0xffff) {
+|			movk reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32
+||		}
+||	} else if (((uintptr_t)(addr) >> 16) & 0xffff) {
+|		movz reg, #(((uintptr_t)(addr) >> 16) & 0xffff), lsl #16
+||		if (((uintptr_t)(addr) >> 32) & 0xffff) {
+|			movk reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32
+||		}
+||	} else {
+|		movz reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32
+||	}
+|.endmacro
+
+// Type cast to unsigned is used to avoid undefined behavior.
+|.macro LOAD_32BIT_VAL, reg, val
+||	if (((uint32_t)(val)) <= MOVZ_IMM) {
+|		movz reg, #((uint32_t)(val))
+||	} else if (((uint32_t)(val) & 0xffff)) {
+|		movz reg, #((uint32_t)(val) & 0xffff)
+||		if ((((uint32_t)(val) >> 16) & 0xffff)) {
+|			movk reg, #(((uint32_t)(val) >> 16) & 0xffff), lsl #16
+||		}
+||	} else {
+|		movz reg, #(((uint32_t)(val) >> 16) & 0xffff), lsl #16
+||	}
+|.endmacro
+
+|.macro LOAD_64BIT_VAL, reg, val
+||	if (((uint64_t)(val)) == 0) {
+|		mov reg, xzr
+||	} else if (((uint64_t)(val)) <= MOVZ_IMM) {
+|		movz reg, #((uint64_t)(val))
+||	} else if (~((uint64_t)(val)) <= MOVZ_IMM) {
+| 		movn reg, #(~((uint64_t)(val)))
+||	} else if ((uint64_t)(val) & 0xffff) {
+|		movz reg, #((uint64_t)(val) & 0xffff)
+||		if (((uint64_t)(val) >> 16) & 0xffff) {
+|			movk reg, #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
+||		}
+||		if (((uint64_t)(val) >> 32) & 0xffff) {
+|			movk reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
+||		}
+||		if ((((uint64_t)(val) >> 48) & 0xffff)) {
+|			movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
+||		}
+||	} else if (((uint64_t)(val) >> 16) & 0xffff) {
+|		movz reg, #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
+||		if (((uint64_t)(val) >> 32) & 0xffff) {
+|			movk reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
+||		}
+||		if ((((uint64_t)(val) >> 48) & 0xffff)) {
+|			movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
+||		}
+||	} else if (((uint64_t)(val) >> 32) & 0xffff) {
+|		movz reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
+||		if ((((uint64_t)(val) >> 48) & 0xffff)) {
+|			movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
+||		}
+||	} else {
+|		movz reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
+||	}
+|.endmacro
+
+// Extract the low 8 bits from 'src_reg' into 'dst_reg'. 0xff can be encoded as imm for 'and' instruction.
+|.macro GET_LOW_8BITS, dst_reg, src_reg
+|	and dst_reg, src_reg, #0xff
+|.endmacro
+
+// Bitwise operation with constants. 'ins' can be and/orr/eor/ands. Operands are 32-bit.
+|.macro BW_OP_32_WITH_CONST, ins, reg, op, val, tmp_reg
+||	if (val == 0) {
+|		ins reg, op, wzr
+||	} else if (logical_immediate_p((uint32_t)val, 32)) {
+|		ins reg, op, #val
+||	} else {
+|		LOAD_32BIT_VAL tmp_reg, val
+|		ins reg, op, tmp_reg
+||	}
+|.endmacro
+
+// Bitwise operation with constants. 'ins' can be and/orr/eor/ands. Operands are 64-bit.
+|.macro BW_OP_64_WITH_CONST, ins, reg, op, val, tmp_reg
+||	if (val == 0) {
+|		ins reg, op, xzr
+||	} else if (logical_immediate_p(val, 64)) {
+|		ins reg, op, #val
+||	} else {
+|		LOAD_64BIT_VAL tmp_reg, val
+|		ins reg, op, tmp_reg
+||	}
+|.endmacro
+
+// Test operation 'tst' with constants. Operands are 32-bit.
+|.macro TST_32_WITH_CONST, reg, val, tmp_reg
+||	if (val == 0) {
+|		tst reg, wzr
+||	} else if (logical_immediate_p((uint32_t)val, 32)) {
+|		tst reg, #val
+||	} else {
+|		LOAD_32BIT_VAL tmp_reg, val
+|		tst reg, tmp_reg
+||	}
+|.endmacro
+
+// Test operation 'tst' with constants. Operands are 64-bit.
+|.macro TST_64_WITH_CONST, reg, val, tmp_reg
+||	if (val == 0) {
+|		tst reg, xzr
+||	} else if (logical_immediate_p(val, 64)) {
+|		tst reg, #val
+||	} else {
+|		LOAD_64BIT_VAL tmp_reg, val
+|		tst reg, tmp_reg
+||	}
+|.endmacro
+
+// Note: 1 is a valid immediate for logical instruction.
+|.macro TST_64_WITH_ONE, reg
+|	tst reg, #1
+|.endmacro
+
+|.macro CMP_32_WITH_CONST, reg, val, tmp_reg
+||	if (val == 0) {
+|		cmp reg, wzr
+||	} else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= CMP_IMM) {
+|		cmp reg, #val
+||	} else if (((int32_t)(val)) < 0 && ((int32_t)(val)) >= -CMP_IMM) {
+|		cmn reg, #-val
+||	} else {
+|		LOAD_32BIT_VAL tmp_reg, val
+|		cmp reg, tmp_reg
+||	}
+|.endmacro
+
+|.macro CMP_64_WITH_CONST_32, reg, val, tmp_reg
+||	if (val == 0) {
+|		cmp reg, xzr
+||	} else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= CMP_IMM) {
+|		cmp reg, #val
+||	} else if (((int32_t)(val)) < 0 && ((int32_t)(val)) >= -CMP_IMM) {
+|		cmn reg, #-val
+||	} else {
+|		LOAD_32BIT_VAL tmp_reg, val
+|		cmp reg, tmp_reg
+||	}
+|.endmacro
+
+|.macro CMP_64_WITH_CONST, reg, val, tmp_reg
+||	if (val == 0) {
+|		cmp reg, xzr
+||	} else if (((int64_t)(val)) > 0 && ((int64_t)(val)) <= CMP_IMM) {
+|		cmp reg, #val
+||	} else if (((int64_t)(val)) < 0 && ((int64_t)(val)) >= -CMP_IMM) {
+|		cmn reg, #-val
+||	} else {
+|		LOAD_64BIT_VAL tmp_reg, val
+|		cmp reg, tmp_reg
+||	}
+|.endmacro
+
+|.macro ADD_SUB_32_WITH_CONST, ins, res_reg, op1_reg, val, tmp_reg
+||	if (val == 0) {
+|		ins res_reg, op1_reg, wzr
+||	} else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= ADD_SUB_IMM) {
+|		ins res_reg, op1_reg, #val
+||	} else {
+|		LOAD_32BIT_VAL tmp_reg, val
+|		ins res_reg, op1_reg, tmp_reg
+||	}
+|.endmacro
+
+|.macro ADD_SUB_64_WITH_CONST_32, ins, res_reg, op1_reg, val, tmp_reg
+||	if (val == 0) {
+|		ins res_reg, op1_reg, xzr
+||	} else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= ADD_SUB_IMM) {
+|		ins res_reg, op1_reg, #val
+||	} else {
+|		LOAD_32BIT_VAL tmp_reg, val
+|		ins res_reg, op1_reg, tmp_reg
+||	}
+|.endmacro
+
+|.macro ADD_SUB_64_WITH_CONST, ins, res_reg, op1_reg, val, tmp_reg
+||	if (val == 0) {
+|		ins res_reg, op1_reg, xzr
+||	} else if (((int64_t)(val)) > 0 && ((int64_t)(val)) <= ADD_SUB_IMM) {
+|		ins res_reg, op1_reg, #val
+||	} else {
+|		LOAD_64BIT_VAL tmp_reg, val
+|		ins res_reg, op1_reg, tmp_reg
+||	}
+|.endmacro
+
+// Safe memory load/store with an unsigned 32-bit offset.
+// 'op' can be used as 'tmp_reg' if 1) 'op' is one GPR, and 2) 'op' != 'base_reg', and 3) ins is 'ldr'.
+|.macro SAFE_MEM_ACC_WITH_UOFFSET, ldr_str_ins, op, base_reg, offset, tmp_reg
+||	if (((uintptr_t)(offset)) > LDR_STR_PIMM64) {
+|		LOAD_32BIT_VAL tmp_reg, offset
+|		ldr_str_ins op, [base_reg, tmp_reg]
+||	} else {
+|		ldr_str_ins op, [base_reg, #(offset)]
+||	}
+|.endmacro
+
+|.macro SAFE_MEM_ACC_WITH_UOFFSET_32, ldr_str_ins, op, base_reg, offset, tmp_reg
+||	if (((uintptr_t)(offset)) > LDR_STR_PIMM32) {
+|		LOAD_32BIT_VAL tmp_reg, offset
+|		ldr_str_ins op, [base_reg, tmp_reg]
+||	} else {
+|		ldr_str_ins op, [base_reg, #(offset)]
+||	}
+|.endmacro
+
+|.macro SAFE_MEM_ACC_WITH_UOFFSET_BYTE, ldrb_strb_ins, op, base_reg, offset, tmp_reg
+||	if (((uintptr_t)(offset)) > LDRB_STRB_PIMM) {
+|		LOAD_32BIT_VAL tmp_reg, offset
+|		ldrb_strb_ins op, [base_reg, tmp_reg]
+||	} else {
+|		ldrb_strb_ins op, [base_reg, #(offset)]
+||	}
+|.endmacro
+
+|.macro LOAD_TSRM_CACHE, reg
+|	.long 0xd53bd051 // TODO: hard-coded: mrs TMP3, tpidr_el0
+||	ZEND_ASSERT(tsrm_ls_cache_tcb_offset <= LDR_STR_PIMM64);
+|	ldr reg, [TMP3, #tsrm_ls_cache_tcb_offset]
+|.endmacro
+
+|.macro LOAD_ADDR_ZTS, reg, struct, field
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		ADD_SUB_64_WITH_CONST_32 add, reg, TMP3, (struct.._offset + offsetof(zend_..struct, field)), reg
+|	.else
+|		LOAD_ADDR reg, &struct.field
+|	.endif
+|.endmacro
+
+// Move the 48-bit address 'addr' into 'tmp_reg' and store it into the dest addr 'op1'
+|.macro ADDR_STORE, op1, addr, tmp_reg
+|	LOAD_ADDR tmp_reg, addr
+|	str tmp_reg, op1
+|.endmacro
+
+// Move the 48-bit address 'addr' into 'tmp_reg1' and compare with the value inside address 'op1'
+|.macro ADDR_CMP, op1, addr, tmp_reg1, tmp_reg2
+|	LOAD_ADDR tmp_reg1, addr
+|	ldr tmp_reg2, op1
+|	cmp tmp_reg2, tmp_reg1
+|.endmacro
+
+// Store the value from a register 'op' into memory 'addr'
+|.macro MEM_STORE, str_ins, op, addr, tmp_reg
+|	LOAD_ADDR tmp_reg, addr
+|	str_ins op, [tmp_reg]
+|.endmacro
+
+// 'op' is 64-bit register
+|.macro MEM_STORE_ZTS, str_ins, op, struct, field, tmp_reg
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		SAFE_MEM_ACC_WITH_UOFFSET str_ins, op, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+|	.else
+|		MEM_STORE str_ins, op, &struct.field, tmp_reg
+|	.endif
+|.endmacro
+
+// 'op' is 32-bit register
+|.macro MEM_STORE_32_ZTS, str_ins, op, struct, field, tmp_reg
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		SAFE_MEM_ACC_WITH_UOFFSET_32 str_ins, op, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+|	.else
+|		MEM_STORE str_ins, op, &struct.field, tmp_reg
+|	.endif
+|.endmacro
+
+|.macro MEM_STORE_BYTE_ZTS, strb_ins, op, struct, field, tmp_reg
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		SAFE_MEM_ACC_WITH_UOFFSET_BYTE strb_ins, op, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+|	.else
+|		MEM_STORE strb_ins, op, &struct.field, tmp_reg
+|	.endif
+|.endmacro
+
+// Load the value from memory 'addr' into a register 'op'
+|.macro MEM_LOAD, ldr_ins, op, addr, tmp_reg
+|	LOAD_ADDR tmp_reg, addr
+|	ldr_ins op, [tmp_reg]
+|.endmacro
+
+|.macro MEM_LOAD_ZTS, ldr_ins, op, struct, field, tmp_reg
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr_ins, op, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+|	.else
+|		MEM_LOAD ldr_ins, op, &struct.field, tmp_reg
+|	.endif
+|.endmacro
+
+|.macro MEM_LOAD_32_ZTS, ldr_ins, op, struct, field, tmp_reg
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		SAFE_MEM_ACC_WITH_UOFFSET_32 ldr_ins, op, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+|	.else
+|		MEM_LOAD ldr_ins, op, &struct.field, tmp_reg
+|	.endif
+|.endmacro
+
+|.macro MEM_LOAD_BYTE_ZTS, ldrb_ins, op, struct, field, tmp_reg
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb_ins, op, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+|	.else
+|		MEM_LOAD ldrb_ins, op, &struct.field, tmp_reg
+|	.endif
+|.endmacro
+
+// Load the value from memory 'addr' into a tmp register 'tmp_reg1',
+// and conduct arithmetic operations with 'op'.
+// Operations can be add/sub/div/mul, and the computation result is stored into 'op'.
+|.macro MEM_LOAD_OP, mem_ins, ldr_ins, op, addr, tmp_reg1, tmp_reg2
+|	MEM_LOAD ldr_ins, tmp_reg1, addr, tmp_reg2
+|	mem_ins op, op, tmp_reg1
+|.endmacro
+
+|.macro MEM_LOAD_OP_ZTS, mem_ins, ldr_ins, op, struct, field, tmp_reg1, tmp_reg2
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr_ins, tmp_reg2, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg1
+|		mem_ins op, op, tmp_reg2
+|	.else
+|		MEM_LOAD_OP mem_ins, ldr_ins, op, &struct.field, tmp_reg1, tmp_reg2
+|	.endif
+|.endmacro
+
+// Load the value from memory 'addr' into a tmp register 'tmp_reg1' and conduct arithmetic operations with 'op'.
+// The computation result is stored back to memory 'addr'. 'op' can be either imm12 or register.
+// For constant case, it should be guaranteed that 'op' can be represented by imm12 before using this macro.
+|.macro MEM_LOAD_OP_STORE, mem_ins, ldr_ins, str_ins, op, addr, tmp_reg1, tmp_reg2
+|	MEM_LOAD ldr_ins, tmp_reg1, addr, tmp_reg2
+|	mem_ins tmp_reg1, tmp_reg1, op
+|	str_ins tmp_reg1, [tmp_reg2]
+|.endmacro
+
+|.macro MEM_LOAD_OP_STORE_ZTS, mem_ins, ldr_ins, str_ins, op, struct, field, tmp_reg1, tmp_reg2
+|	.if ZTS
+|		LOAD_TSRM_CACHE TMP3
+||		if (((uintptr_t)(struct.._offset+offsetof(zend_..struct, field))) > LDR_STR_PIMM64) {
+|			LOAD_32BIT_VAL tmp_reg1, (struct.._offset+offsetof(zend_..struct, field))
+|			ldr_ins tmp_reg2, [TMP3, tmp_reg1]
+|			mem_ins tmp_reg2, tmp_reg2, op
+|			str_ins tmp_reg2, [TMP3, tmp_reg1]
+||		} else {
+|			ldr_ins tmp_reg2, [TMP3, #(struct.._offset+offsetof(zend_..struct, field))]
+|			mem_ins tmp_reg2, tmp_reg2, op
+|			str_ins tmp_reg2, [TMP3, #(struct.._offset+offsetof(zend_..struct, field))]
+||		}
+|	.else
+|		MEM_LOAD_OP_STORE mem_ins, ldr_ins, str_ins, op, &struct.field, tmp_reg1, tmp_reg2
+|	.endif
+|.endmacro
+
+|.macro LOAD_BASE_ADDR, reg, base, offset
+||	if (offset) {
+|		ADD_SUB_64_WITH_CONST_32 add, reg, Rx(base), offset, reg
+||	} else {
+||		if (base == ZREG_RSP) {
+|			mov reg, sp
+||		} else {
+|			mov reg, Rx(base)
+||		}
+||	}
+|.endmacro
+
+|.macro EXT_CALL, func, tmp_reg
+||	if (arm64_may_use_b(func)) {
+|		bl &func
+||	} else {
+|		LOAD_ADDR tmp_reg, func
+|		blr tmp_reg
+||	}
+|.endmacro
+
+|.macro EXT_JMP, func, tmp_reg
+||	if (arm64_may_use_b(func)) {
+|		b &func
+||	} else {
+|		LOAD_ADDR tmp_reg, func
+|		br tmp_reg
+||	}
+|.endmacro
+
+|.macro SAVE_IP
+||	if (GCC_GLOBAL_REGS) {
+|		str IP, EX->opline
+||	}
+|.endmacro
+
+|.macro LOAD_IP
+||	if (GCC_GLOBAL_REGS) {
+|		ldr IP, EX->opline
+||	}
+|.endmacro
+
+|.macro LOAD_IP_ADDR, addr
+||	if (GCC_GLOBAL_REGS) {
+|		LOAD_ADDR IP, addr
+||	} else {
+|		ADDR_STORE EX->opline, addr, RX
+||	}
+|.endmacro
+
+|.macro LOAD_IP_ADDR_ZTS, struct, field, tmp_reg
+|	.if ZTS
+||		if (GCC_GLOBAL_REGS) {
+|			LOAD_TSRM_CACHE IP
+|	   		SAFE_MEM_ACC_WITH_UOFFSET ldr, IP, IP, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+||		} else {
+|			LOAD_TSRM_CACHE RX
+|			ADD_SUB_64_WITH_CONST_32 add, RX, RX, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
+|			str RX, EX->opline
+||		}
+|	.else
+|		LOAD_IP_ADDR &struct.field
+|	.endif
+|.endmacro
+
+|.macro GET_IP, reg
+||	if (GCC_GLOBAL_REGS) {
+|		mov reg, IP
+||	} else {
+|		ldr reg, EX->opline
+||	}
+|.endmacro
+
+// In x86 implementation, 'val' can be either a constant or a register.
+// In AArch64, use ADD_IP for register case,
+// and use ADD_IP_FROM_CST for constant case, where the value can be represented by ADD_SUB_IMM.
+|.macro ADD_IP, val, tmp_reg
+||	if (GCC_GLOBAL_REGS) {
+|		add IP, IP, val
+||	} else {
+|		ldr tmp_reg, EX->opline
+|		add tmp_reg, tmp_reg, val
+|		str tmp_reg, EX->opline
+||	}
+|.endmacro
+
+|.macro ADD_IP_SHIFT, val, shift, tmp_reg
+||	if (GCC_GLOBAL_REGS) {
+|		add IP, IP, val, shift
+||	} else {
+|		ldr tmp_reg, EX->opline
+|		add tmp_reg, tmp_reg, val, shift
+|		str tmp_reg, EX->opline
+||	}
+|.endmacro
+
+|.macro ADD_IP_FROM_CST, val, tmp_reg
+||	ZEND_ASSERT(val >=0 && val <= ADD_SUB_IMM);
+||	if (GCC_GLOBAL_REGS) {
+|		add IP, IP, #val
+||	} else {
+|		ldr tmp_reg, EX->opline
+|		add tmp_reg, tmp_reg, #val
+|		str tmp_reg, EX->opline
+||	}
+|.endmacro
+
+|.macro JMP_IP, tmp_reg
+||	if (GCC_GLOBAL_REGS) {
+|		ldr tmp_reg, [IP]
+|		br tmp_reg
+||	} else {
+|		ldr tmp_reg, EX:CARG1->opline
+|		br tmp_reg
+||	}
+|.endmacro
+
+|.macro CMP_IP, addr, tmp_reg1, tmp_reg2
+|	LOAD_ADDR tmp_reg1, addr
+||	if (GCC_GLOBAL_REGS) {
+|		cmp IP, tmp_reg1
+||	} else {
+|		ldr tmp_reg2, EX->opline
+|		cmp tmp_reg2, tmp_reg1
+||	}
+|.endmacro
+
+|.macro LOAD_ZVAL_ADDR, reg, addr
+||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
+|		LOAD_ADDR reg, Z_ZV(addr)
+||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|		LOAD_BASE_ADDR reg, Z_REG(addr), Z_OFFSET(addr)
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+|.macro GET_Z_TYPE_INFO, reg, zv
+|	ldr reg, [zv, #offsetof(zval,u1.type_info)]
+|.endmacro
+
+|.macro SET_Z_TYPE_INFO, zv, type, tmp_reg
+|	LOAD_32BIT_VAL tmp_reg, type
+|	str tmp_reg, [zv, #offsetof(zval,u1.type_info)]
+|.endmacro
+
+|.macro GET_ZVAL_TYPE, reg, addr, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.v.type), tmp_reg
+|.endmacro
+
+// 'reg' is 32-bit register
+|.macro GET_ZVAL_TYPE_INFO, reg, addr, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_32 ldr, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg
+|.endmacro
+
+|.macro SET_ZVAL_TYPE_INFO, addr, type, tmp_reg1, tmp_reg2
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	LOAD_32BIT_VAL tmp_reg1, type
+|	SAFE_MEM_ACC_WITH_UOFFSET_32 str, tmp_reg1, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg2
+|.endmacro
+
+// 'type' is 32-bit register
+|.macro SET_ZVAL_TYPE_INFO_FROM_REG, addr, type, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_32 str, type, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg
+|.endmacro
+
+|.macro GET_Z_PTR, reg, zv
+|	ldr reg, [zv]
+|.endmacro
+
+|.macro SET_Z_PTR, zv, val
+|	mov aword [zv], val
+|.endmacro
+
+|.macro GET_Z_W2, reg, zv
+|	mov reg, dword [zv+4]
+|.endmacro
+
+|.macro SET_Z_W2, zv, reg
+|	mov dword [zv+4], reg
+|.endmacro
+
+|.macro GET_ZVAL_PTR, reg, addr, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET ldr, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
+|.endmacro
+
+|.macro SET_ZVAL_PTR, addr, val, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET str, val, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
+|.endmacro
+
+|.macro UNDEF_OPLINE_RESULT, tmp_reg
+|	ldr REG0, EX->opline
+|	ldr REG0w, OP:REG0->result.var
+|	add REG0, FP, REG0
+|	SET_Z_TYPE_INFO REG0, IS_UNDEF, tmp_reg
+|.endmacro
+
+// Define DOUBLE_CMP to replace SSE_AVX_OP and SSE_OP for comparions in x86 implementation.
+// Operand1 is from 'reg', and operand2 is from 'addr'.
+|.macro DOUBLE_CMP, reg, addr, tmp_reg, fp_tmp_reg
+||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
+|		LOAD_ADDR Rx(tmp_reg), Z_ZV(addr)
+|		ldr Rd(fp_tmp_reg-ZREG_V0), [Rx(tmp_reg)]
+|		fcmp Rd(reg-ZREG_V0), Rd(fp_tmp_reg-ZREG_V0)
+||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr, Rd(fp_tmp_reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg)
+|		fcmp Rd(reg-ZREG_V0), Rd(fp_tmp_reg-ZREG_V0)
+||	} else if (Z_MODE(addr) == IS_REG) {
+|		fcmp Rd(reg-ZREG_V0), Rd(Z_REG(addr)-ZREG_V0)
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+// Define DOUBLE_GET_LONG to replace SSE_GET_LONG in x86 implementation.
+// Convert the LONG value 'lval' into DOUBLE type, and move it into 'reg'
+|.macro DOUBLE_GET_LONG, reg, lval, tmp_reg
+||	if (lval == 0) {
+|		mov Rx(tmp_reg), xzr
+|		fmov Rd(reg-ZREG_V0), Rx(tmp_reg)
+||	} else {
+|		LOAD_64BIT_VAL Rx(tmp_reg), lval
+|		scvtf Rd(reg-ZREG_V0), Rx(tmp_reg)
+||	}
+|.endmacro
+
+// Define DOUBLE_GET_ZVAL_LVAL to replace SSE_GET_ZVAL_LVAL in x86 implementation.
+// Convert the LONG value in 'addr' into DOUBLE type, and move it into 'reg'
+|.macro DOUBLE_GET_ZVAL_LVAL, reg, addr, tmp_reg1, tmp_reg2
+||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
+|		DOUBLE_GET_LONG reg, Z_LVAL_P(Z_ZV(addr)), tmp_reg1
+||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr, Rx(tmp_reg1), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg2)
+|		scvtf Rd(reg-ZREG_V0), Rx(tmp_reg1)
+||	} else if (Z_MODE(addr) == IS_REG) {
+|		scvtf Rd(reg-ZREG_V0), Rx(Z_REG(addr))
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+// Define DOUBLE_MATH_REG to replace AVX_MATH_REG in x86 implementation.
+|.macro DOUBLE_MATH_REG, opcode, dst_reg, op1_reg, op2_reg
+||	switch (opcode) {
+||		case ZEND_ADD:
+|			fadd Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
+||			break;
+||		case ZEND_SUB:
+|			fsub Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
+||			break;
+||		case ZEND_MUL:
+|			fmul Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
+||			break;
+||		case ZEND_DIV:
+|			fdiv Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
+||			break;
+||	}
+|.endmacro
+
+// Define LONG_ADD_SUB, LONG_BW_OP, LONG_MUL, LONG_CMP to replace the LONG_OP in x86 implementation.
+// 'long_ins' should be addition or subtraction.
+|.macro LONG_ADD_SUB, long_ins, reg, addr, tmp_reg1
+||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
+|		ADD_SUB_64_WITH_CONST long_ins, Rx(reg), Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg1
+||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr, tmp_reg1, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg1
+|		long_ins Rx(reg), Rx(reg), tmp_reg1
+||	} else if (Z_MODE(addr) == IS_REG) {
+|		long_ins Rx(reg), Rx(reg), Rx(Z_REG(addr))
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+// 'long_ins' should be 'and', 'orr' or 'eor'
+|.macro LONG_BW_OP, long_ins, reg, addr, tmp_reg1
+||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
+|		BW_OP_64_WITH_CONST long_ins, Rx(reg), Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg1
+||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr, tmp_reg1, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg1
+|		long_ins Rx(reg), Rx(reg), tmp_reg1
+||	} else if (Z_MODE(addr) == IS_REG) {
+|		long_ins Rx(reg), Rx(reg), Rx(Z_REG(addr))
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+|.macro LONG_CMP, reg, addr, tmp_reg
+||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
+|		CMP_64_WITH_CONST Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg
+||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr, tmp_reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
+|		cmp Rx(reg), tmp_reg
+||	} else if (Z_MODE(addr) == IS_REG) {
+|		cmp Rx(reg), Rx(Z_REG(addr))
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+// long_ins should be addition or subtraction.
+|.macro LONG_ADD_SUB_WITH_IMM, long_ins, op1_addr, lval, tmp_reg1, tmp_reg2
+||	ZEND_ASSERT(lval >=0 && lval <= ADD_SUB_IMM);
+||	if (Z_MODE(op1_addr) == IS_MEM_ZVAL) {
+||		if (((uint32_t)(Z_OFFSET(op1_addr))) > LDR_STR_PIMM64) {
+|			LOAD_32BIT_VAL tmp_reg2, Z_OFFSET(op1_addr)
+|			ldr tmp_reg1, [Rx(Z_REG(op1_addr)), tmp_reg2]
+|			long_ins tmp_reg1, tmp_reg1, #lval
+|			str tmp_reg1, [Rx(Z_REG(op1_addr)), tmp_reg2]
+||		} else {
+|			ldr tmp_reg1, [Rx(Z_REG(op1_addr)), #Z_OFFSET(op1_addr)]
+|			long_ins tmp_reg1, tmp_reg1, #lval
+|			str tmp_reg1, [Rx(Z_REG(op1_addr)), #Z_OFFSET(op1_addr)]
+||		}
+||	} else if (Z_MODE(op1_addr) == IS_REG) {
+|		long_ins Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr)), #lval
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+// Define LONG_CMP_WITH_CONST to replace LONG_OP_WITH_CONST in the x86 implementation.
+// Note that the 'long_ins' in all use sites of LONG_OP_WITH_CONST are always 'cmp'.
+// Note that this macro is different from LONG_CMP.
+|.macro LONG_CMP_WITH_CONST, op1_addr, lval, tmp_reg1, tmp_reg2
+||	if (Z_MODE(op1_addr) == IS_MEM_ZVAL) {
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr, tmp_reg1, Rx(Z_REG(op1_addr)), Z_OFFSET(op1_addr), tmp_reg2
+|		CMP_64_WITH_CONST tmp_reg1, lval, tmp_reg2
+||	} else if (Z_MODE(op1_addr) == IS_REG) {
+|		CMP_64_WITH_CONST Rx(Z_REG(op1_addr)), lval, tmp_reg1
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+|.macro GET_ZVAL_LVAL, reg, addr, tmp_reg
+||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
+||		if (Z_LVAL_P(Z_ZV(addr)) == 0) {
+|			mov Rx(reg), xzr
+||		} else {
+|			LOAD_64BIT_VAL Rx(reg), Z_LVAL_P(Z_ZV(addr))
+||		}
+||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|		SAFE_MEM_ACC_WITH_UOFFSET ldr, Rx(reg), Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
+||	} else if (Z_MODE(addr) == IS_REG) {
+||		if (reg != Z_REG(addr)) {
+|			mov Rx(reg), Rx(Z_REG(addr))
+||		}
+||	} else {
+||		ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+|.macro LONG_MATH, opcode, reg, addr, tmp_reg1
+||	switch (opcode) {
+||		case ZEND_ADD:
+|			LONG_ADD_SUB  adds, reg, addr, tmp_reg1
+||			break;
+||		case ZEND_SUB:
+|			LONG_ADD_SUB  subs, reg, addr, tmp_reg1
+||			break;
+||		case ZEND_BW_OR:
+|			LONG_BW_OP  orr, reg, addr, tmp_reg1
+||			break;
+||		case ZEND_BW_AND:
+|			LONG_BW_OP  and, reg, addr, tmp_reg1
+||			break;
+||		case ZEND_BW_XOR:
+|			LONG_BW_OP  eor, reg, addr, tmp_reg1
+||			break;
+||		default:
+||			ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+|.macro LONG_MATH_REG, opcode, dst_reg, src_reg1, src_reg2
+||	switch (opcode) {
+||		case ZEND_ADD:
+|			adds dst_reg, src_reg1, src_reg2
+||			break;
+||		case ZEND_SUB:
+|			subs dst_reg, src_reg1, src_reg2
+||			break;
+||		case ZEND_BW_OR:
+|			orr dst_reg, src_reg1, src_reg2
+||			break;
+||		case ZEND_BW_AND:
+|			and dst_reg, src_reg1, src_reg2
+||			break;
+||		case ZEND_BW_XOR:
+|			eor dst_reg, src_reg1, src_reg2
+||			break;
+||		default:
+||			ZEND_UNREACHABLE();
+||	}
+|.endmacro
+
+// In x86 implementation, argument 'lval' of SET_ZVAL_LVAL can be either a LONG constant
+// or a register. Here, we separate it into two macros, SET_ZVAL_LVAL for the consant case,
+// and SET_ZVAL_LVAL_FROM_REG for the register case.
+|.macro SET_ZVAL_LVAL_FROM_REG, addr, reg, tmp_reg
+||	if (Z_MODE(addr) == IS_REG) {
+|		mov Rx(Z_REG(addr)), reg
+||	} else {
+||		ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|		SAFE_MEM_ACC_WITH_UOFFSET str, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
+||	}
+|.endmacro
+
+|.macro SET_ZVAL_LVAL, addr, lval, tmp_reg1, tmp_reg2
+||	if (lval == 0) {
+|		SET_ZVAL_LVAL_FROM_REG addr, xzr, tmp_reg2
+||	} else {
+|		LOAD_64BIT_VAL tmp_reg1, lval
+|		SET_ZVAL_LVAL_FROM_REG addr, tmp_reg1, tmp_reg2
+||	}
+|.endmacro
+
+// Define SET_ZVAL_DVAL to replace SSE_SET_ZVAL_DVAL in x86 implementation.
+|.macro SET_ZVAL_DVAL, addr, reg, tmp_reg
+||	if (Z_MODE(addr) == IS_REG) {
+||		if (reg != Z_REG(addr)) {
+|			fmov Rd(Z_REG(addr)-ZREG_V0), Rd(reg-ZREG_V0)
+||		}
+||	} else {
+||		ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|		SAFE_MEM_ACC_WITH_UOFFSET str, Rd(reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg)
+||	}
+|.endmacro
+
+// Define GET_ZVAL_DVAL to replace SSE_GET_ZVAL_DVAL in x86 implementation.
+|.macro GET_ZVAL_DVAL, reg, addr, tmp_reg
+||	if (Z_MODE(addr) != IS_REG || reg != Z_REG(addr)) {
+||		if (Z_MODE(addr) == IS_CONST_ZVAL) {
+|			LOAD_ADDR Rx(tmp_reg), Z_ZV(addr)
+|			ldr Rd(reg-ZREG_V0), [Rx(tmp_reg)]
+||		} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
+|			SAFE_MEM_ACC_WITH_UOFFSET ldr, Rd(reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg)
+||		} else if (Z_MODE(addr) == IS_REG) {
+|			fmov Rd(reg-ZREG_V0), Rd(Z_REG(addr)-ZREG_V0)
+||		} else {
+||			ZEND_UNREACHABLE();
+||		}
+||	}
+|.endmacro
+
+|.macro ZVAL_COPY_CONST, dst_addr, dst_info, dst_def_info, zv, tmp_reg1, tmp_reg2, fp_tmp_reg
+||	if (Z_TYPE_P(zv) > IS_TRUE) {
+||		if (Z_TYPE_P(zv) == IS_DOUBLE) {
+||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : fp_tmp_reg;
+|			LOAD_ADDR Rx(tmp_reg1), zv
+|			ldr Rd(dst_reg-ZREG_V0), [Rx(tmp_reg1)]
+|			SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2
+||		} else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) {
+||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : fp_tmp_reg;
+|			DOUBLE_GET_LONG dst_reg, Z_LVAL_P(zv), tmp_reg1
+|			SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2
+||		} else {
+|			// In x64, if the range of this LONG value can be represented via INT type, only move the low 32 bits into dst_addr.
+|			// Note that imm32 is signed extended to 64 bits during mov.
+|			// In aarch64, we choose to handle both cases in the same way. Even though 4 mov's are used for 64-bit value and 2 mov's are
+|			// needed for 32-bit value, an extra ext insn is needed for 32-bit vlaue.
+|			SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
+||		}
+||	}
+||	if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
+||		if (dst_def_info == MAY_BE_DOUBLE) {
+||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
+|				SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2)
+||			}
+||		} else if (((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (1<<Z_TYPE_P(zv))) || (dst_info & (MAY_BE_STRING|MAY_BE_ARRAY)) != 0) {
+|			SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2)
+||		}
+||	}
+|.endmacro
+
+|.macro ZVAL_COPY_CONST_2, dst_addr, res_addr, dst_info, dst_def_info, zv, tmp_reg1, tmp_reg2, fp_tmp_reg
+||	if (Z_TYPE_P(zv) > IS_TRUE) {
+||		if (Z_TYPE_P(zv) == IS_DOUBLE) {
+||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ?
+||				Z_REG(dst_addr) : ((Z_MODE(res_addr) == IS_REG) ? Z_MODE(res_addr) : fp_tmp_reg);
+|			LOAD_ADDR Rx(tmp_reg1), zv
+|			ldr Rd(dst_reg-ZREG_V0), [Rx(tmp_reg1)]
+|			SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2
+|			SET_ZVAL_DVAL res_addr, dst_reg, tmp_reg2
+||		} else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) {
+||			if (Z_MODE(dst_addr) == IS_REG) {
+|				DOUBLE_GET_LONG Z_REG(dst_addr), Z_LVAL_P(zv), tmp_reg1
+|				SET_ZVAL_DVAL res_addr, Z_REG(dst_addr), tmp_reg2
+||			} else if (Z_MODE(res_addr) == IS_REG) {
+|				DOUBLE_GET_LONG Z_REG(res_addr), Z_LVAL_P(zv), tmp_reg1
+|				SET_ZVAL_DVAL dst_addr, Z_REG(res_addr), tmp_reg2
+||			} else {
+|				DOUBLE_GET_LONG fp_tmp_reg, Z_LVAL_P(zv), tmp_reg1
+|				SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg2
+|				SET_ZVAL_DVAL res_addr, fp_tmp_reg, tmp_reg2
+||			}
+||		} else {
+||			if (Z_MODE(dst_addr) == IS_REG) {
+|				SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
+|				SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(dst_addr)), Rx(tmp_reg1)
+||			} else if (Z_MODE(res_addr) == IS_REG) {
+|				SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
+|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(res_addr)), Rx(tmp_reg1)
+||			} else {
+|				SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
+|				SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
+||			}
+||		}
+||	}
+||	if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
+||		if (dst_def_info == MAY_BE_DOUBLE) {
+||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
+|				SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2)
+||			}
+||		} else if (((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (1<<Z_TYPE_P(zv))) || (dst_info & (MAY_BE_STRING|MAY_BE_ARRAY)) != 0) {
+|			SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2)
+||		}
+||	}
+||	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+||		if (dst_def_info == MAY_BE_DOUBLE) {
+|			SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2)
+||		} else {
+|			SET_ZVAL_TYPE_INFO res_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2)
+||		}
+||	}
+|.endmacro
+
+// the same as above, but "src" may overlap with "reg1"
+// Useful info would be stored into reg1 and reg2, and they might be used afterward.
+|.macro ZVAL_COPY_VALUE, dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg1, tmp_reg2, fp_tmp_reg
+|	ZVAL_COPY_VALUE_V dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg1, fp_tmp_reg
+||	if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+||      !(src_info & MAY_BE_GUARD) &&
+||		has_concrete_type(src_info & MAY_BE_ANY)) {
+||		if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
+||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD))) {
+||				uint32_t type = concrete_type(src_info);
+|				SET_ZVAL_TYPE_INFO dst_addr, type, Rw(tmp_reg1), Rx(tmp_reg2)
+||			}
+||		}
+||	} else {
+|		GET_ZVAL_TYPE_INFO Rw(reg1), src_addr, Rx(tmp_reg1)
+|		SET_ZVAL_TYPE_INFO_FROM_REG dst_addr, Rw(reg1), Rx(tmp_reg1)
+||	}
+|.endmacro
+
+|.macro ZVAL_COPY_VALUE_V, dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg, fp_tmp_reg
+||	if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
+||		if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_LONG) {
+||			if (Z_MODE(src_addr) == IS_REG) {
+||				if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) {
+|					SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg)
+||				}
+||			} else if (Z_MODE(dst_addr) == IS_REG) {
+|				GET_ZVAL_LVAL Z_REG(dst_addr), src_addr, Rx(tmp_reg)
+||			} else {
+|				GET_ZVAL_LVAL reg2, src_addr, Rx(tmp_reg)
+|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(reg2), Rx(tmp_reg)
+||			}
+||		} else if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
+||			if (Z_MODE(src_addr) == IS_REG) {
+|				SET_ZVAL_DVAL dst_addr, Z_REG(src_addr), tmp_reg
+||			} else if (Z_MODE(dst_addr) == IS_REG) {
+|				GET_ZVAL_DVAL Z_REG(dst_addr), src_addr, tmp_reg
+||			} else {
+|				GET_ZVAL_DVAL fp_tmp_reg, src_addr, tmp_reg
+|				SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg
+||			}
+||		// Combine the following two branches.
+||		// } else if (!(src_info & (MAY_BE_DOUBLE|MAY_BE_GUARD))) {
+||		} else {
+|			GET_ZVAL_PTR Rx(reg2), src_addr, Rx(tmp_reg)
+|			SET_ZVAL_PTR dst_addr, Rx(reg2), Rx(tmp_reg)
+||		}
+||	}
+|.endmacro
+
+|.macro ZVAL_COPY_VALUE_2, dst_addr, dst_info, res_addr, src_addr, src_info, reg1, reg2, tmp_reg, tmp_reg2, fp_tmp_reg
+||	if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
+||		if ((src_info & MAY_BE_ANY) == MAY_BE_LONG) {
+||			if (Z_MODE(src_addr) == IS_REG) {
+||				if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) {
+|					SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg)
+||				}
+||				if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(src_addr)) {
+|					SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg)
+||				}
+||			} else if (Z_MODE(dst_addr) == IS_REG) {
+|				GET_ZVAL_LVAL Z_REG(dst_addr), src_addr, Rx(tmp_reg)
+||				if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(dst_addr)) {
+|					SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(dst_addr)), Rx(tmp_reg)
+||				}
+||			} else if (Z_MODE(res_addr) == IS_REG) {
+|				GET_ZVAL_LVAL Z_REG(res_addr), src_addr, Rx(tmp_reg)
+|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(res_addr)), Rx(tmp_reg)
+||			} else {
+|				GET_ZVAL_LVAL reg2, src_addr, Rx(tmp_reg)
+|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(reg2), Rx(tmp_reg)
+|				SET_ZVAL_LVAL_FROM_REG res_addr, Rx(reg2), Rx(tmp_reg)
+||			}
+||		} else if ((src_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+||			if (Z_MODE(src_addr) == IS_REG) {
+|				SET_ZVAL_DVAL dst_addr, Z_REG(src_addr), tmp_reg
+|				SET_ZVAL_DVAL res_addr, Z_REG(src_addr), tmp_reg
+||			} else if (Z_MODE(dst_addr) == IS_REG) {
+|				GET_ZVAL_DVAL Z_REG(dst_addr), src_addr, tmp_reg
+|				SET_ZVAL_DVAL res_addr, Z_REG(dst_addr), tmp_reg
+||			} else if (Z_MODE(res_addr) == IS_REG) {
+|				GET_ZVAL_DVAL Z_REG(res_addr), src_addr, tmp_reg
+|				SET_ZVAL_DVAL dst_addr, Z_REG(res_addr), tmp_reg
+||			} else {
+|				GET_ZVAL_DVAL fp_tmp_reg, src_addr, tmp_reg
+|				SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg
+|				SET_ZVAL_DVAL res_addr, fp_tmp_reg, tmp_reg
+||			}
+||		} else {
+|			GET_ZVAL_PTR Rx(reg2), src_addr, Rx(tmp_reg)
+|			SET_ZVAL_PTR dst_addr, Rx(reg2), Rx(tmp_reg)
+|			SET_ZVAL_PTR res_addr, Rx(reg2), Rx(tmp_reg)
+||		}
+||	}
+||	if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+||	    has_concrete_type(src_info & MAY_BE_ANY)) {
+||		uint32_t type = concrete_type(src_info);
+||		if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
+||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF))) {
+|				SET_ZVAL_TYPE_INFO dst_addr, type, Rw(tmp_reg), Rx(tmp_reg2)
+||			}
+||		}
+||		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+|			SET_ZVAL_TYPE_INFO res_addr, type, Rw(tmp_reg), Rx(tmp_reg2)
+||		}
+||	} else {
+|		GET_ZVAL_TYPE_INFO Rw(reg1), src_addr, Rx(tmp_reg)
+|		SET_ZVAL_TYPE_INFO_FROM_REG dst_addr, Rw(reg1), Rx(tmp_reg)
+|		SET_ZVAL_TYPE_INFO_FROM_REG res_addr, Rw(reg1), Rx(tmp_reg)
+||	}
+|.endmacro
+
+|.macro IF_UNDEF, type_reg, label
+|	cbz type_reg, label
+|.endmacro
+
+|.macro IF_TYPE, type, val, label
+||	if (val == 0) {
+|		cbz type, label
+||	} else {
+|		cmp type, #val
+|		beq label
+||	}
+|.endmacro
+
+|.macro IF_NOT_TYPE, type, val, label
+||	if (val == 0) {
+|		cbnz type, label
+||	} else {
+|		cmp type, #val
+|		bne label
+||	}
+|.endmacro
+
+|.macro IF_Z_TYPE, zv, val, label, tmp_reg
+|	ldrb tmp_reg, [zv, #offsetof(zval, u1.v.type)]
+|	IF_TYPE tmp_reg, val, label
+|.endmacro
+
+|.macro IF_NOT_Z_TYPE, zv, val, label, tmp_reg
+|	ldrb tmp_reg, [zv, #offsetof(zval, u1.v.type)]
+|	IF_NOT_TYPE tmp_reg, val, label
+|.endmacro
+
+|.macro CMP_ZVAL_TYPE, addr, val, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg)
+|	cmp Rw(tmp_reg), #val
+|.endmacro
+
+|.macro IF_ZVAL_TYPE, addr, val, label, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg)
+|	IF_TYPE Rw(tmp_reg), val, label
+|.endmacro
+
+|.macro IF_NOT_ZVAL_TYPE, addr, val, label, tmp_reg
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg)
+|	IF_NOT_TYPE Rw(tmp_reg), val, label
+|.endmacro
+
+|.macro IF_FLAGS, type_flags, mask, label, tmp_reg
+|	TST_32_WITH_CONST type_flags, mask, tmp_reg
+|	bne label
+|.endmacro
+
+|.macro IF_NOT_FLAGS, type_flags, mask, label, tmp_reg
+|	TST_32_WITH_CONST type_flags, mask, tmp_reg
+|	beq label
+|.endmacro
+
+|.macro IF_REFCOUNTED, type_flags, label, tmp_reg
+|	TST_32_WITH_CONST type_flags, (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), tmp_reg
+|	bne label
+|.endmacro
+
+|.macro IF_NOT_REFCOUNTED, type_flags, label, tmp_reg
+|	TST_32_WITH_CONST type_flags, (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), tmp_reg
+|	beq label
+|.endmacro
+
+|.macro IF_ZVAL_FLAGS, addr, mask, label, tmp_reg1, tmp_reg2
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, Rw(tmp_reg1), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type_flags), Rx(tmp_reg2)
+|	IF_FLAGS Rw(tmp_reg1), mask, label, Rw(tmp_reg2)
+|.endmacro
+
+|.macro IF_NOT_ZVAL_FLAGS, addr, mask, label, tmp_reg1, tmp_reg2
+||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
+|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, Rw(tmp_reg1), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type_flags), Rx(tmp_reg2)
+|	IF_NOT_FLAGS Rw(tmp_reg1), mask, label, Rw(tmp_reg2)
+|.endmacro
+
+|.macro IF_ZVAL_REFCOUNTED, addr, label, tmp_reg1, tmp_reg2
+|	IF_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label, tmp_reg1, tmp_reg2
+|.endmacro
+
+|.macro IF_NOT_ZVAL_REFCOUNTED, addr, label, tmp_reg1, tmp_reg2
+|	IF_NOT_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label, tmp_reg1, tmp_reg2
+|.endmacro
+
+|.macro IF_NOT_ZVAL_COLLECTABLE, addr, label, tmp_reg1, tmp_reg2
+|	IF_NOT_ZVAL_FLAGS addr, IS_TYPE_COLLECTABLE, label, tmp_reg1, tmp_reg2
+|.endmacro
+
+|.macro GC_ADDREF, zv, tmp_reg
+|	ldr tmp_reg, [zv]
+|	add tmp_reg, tmp_reg, #1
+|	str tmp_reg, [zv]
+|.endmacro
+
+|.macro GC_ADDREF_2, zv, tmp_reg
+|	ldr tmp_reg, [zv]
+|	add tmp_reg, tmp_reg, #2
+|	str tmp_reg, [zv]
+|.endmacro
+
+|.macro GC_DELREF, zv, tmp_reg
+|	ldr tmp_reg, [zv]
+|	subs tmp_reg, tmp_reg, #1
+|	str tmp_reg, [zv]
+|.endmacro
+
+|.macro IF_GC_MAY_NOT_LEAK, ptr, label, tmp_reg1, tmp_reg2
+|	ldr tmp_reg1, [ptr, #4]
+|	TST_32_WITH_CONST tmp_reg1, (GC_INFO_MASK | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)), tmp_reg2
+|	bne label
+|.endmacro
+
+|.macro ADDREF_CONST, zv, tmp_reg1, tmp_reg2
+|	LOAD_ADDR tmp_reg1, Z_PTR_P(zv)
+|	ldr tmp_reg2, [tmp_reg1]
+|	add tmp_reg2, tmp_reg2, #1
+|	str tmp_reg2, [tmp_reg1]
+|.endmacro
+
+|.macro ADDREF_CONST_2, zv, tmp_reg1, tmp_reg2
+|	LOAD_ADDR tmp_reg1, Z_PTR_P(zv)
+|	ldr tmp_reg2, [tmp_reg1]
+|	add tmp_reg2, tmp_reg2, #2
+|	str tmp_reg2, [tmp_reg1]
+|.endmacro
+
+|.macro TRY_ADDREF, val_info, type_flags_reg, value_ptr_reg, tmp_reg
+||	if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+||		if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+|			IF_NOT_REFCOUNTED type_flags_reg, >1, tmp_reg
+||		}
+|		GC_ADDREF value_ptr_reg, tmp_reg
+|1:
+||	}
+|.endmacro
+
+|.macro TRY_ADDREF_2, val_info, type_flags_reg, value_ptr_reg, tmp_reg
+||	if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+||		if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+|			IF_NOT_REFCOUNTED type_flags_reg, >1, tmp_reg
+||		}
+|		ldr tmp_reg, [value_ptr_reg]
+|		add tmp_reg, tmp_reg, #2
+|		str tmp_reg, [value_ptr_reg]
+|1:
+||	}
+|.endmacro
+
+|.macro ZVAL_DEREF, reg, info, tmp_reg
+||	if (info & MAY_BE_REF) {
+|		IF_NOT_Z_TYPE, reg, IS_REFERENCE, >1, tmp_reg
+|		GET_Z_PTR reg, reg
+|		add reg, reg, #offsetof(zend_reference, val)
+|1:
+||	}
+|.endmacro
+
+|.macro SET_EX_OPLINE, op, tmp_reg
+||	if (op == last_valid_opline) {
+||		zend_jit_use_last_valid_opline();
+|		SAVE_IP
+||	} else {
+|		ADDR_STORE EX->opline, op, tmp_reg
+||		if (!GCC_GLOBAL_REGS) {
+||			zend_jit_reset_last_valid_opline();
+||		}
+||	}
+|.endmacro
+
+// arg1 "zval" should be in FCARG1x
+|.macro ZVAL_DTOR_FUNC, var_info, opline, tmp_reg
+||	do {
+||		if (has_concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_INDIRECT))) {
+||			zend_uchar type = concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+||			if (type == IS_STRING && !ZEND_DEBUG) {
+|				EXT_CALL _efree, tmp_reg
+||				break;
+||			} else if (type == IS_ARRAY) {
+||				if ((var_info) & (MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_REF)) {
+||					if (opline && ((var_info) & (MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_REF))) {
+|						SET_EX_OPLINE opline, tmp_reg
+||					}
+|					EXT_CALL zend_array_destroy, tmp_reg
+||				} else {
+|					EXT_CALL zend_jit_array_free, tmp_reg
+||				}
+||				break;
+||			} else if (type == IS_OBJECT) {
+||				if (opline) {
+|					SET_EX_OPLINE opline, REG0
+||				}
+|				EXT_CALL zend_objects_store_del, tmp_reg
+||				break;
+||			}
+||		}
+||		if (opline) {
+|			SET_EX_OPLINE opline, tmp_reg
+||		}
+|		EXT_CALL rc_dtor_func, tmp_reg
+||	} while(0);
+|.endmacro
+
+|.macro ZVAL_PTR_DTOR, addr, op_info, gc, cold, opline, tmp_reg1, tmp_reg2
+||	if ((op_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+||		if ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+|			// if (Z_REFCOUNTED_P(cv)) {
+||			if (cold) {
+|				IF_ZVAL_REFCOUNTED addr, >1, tmp_reg1, tmp_reg2
+|.cold_code
+|1:
+||			} else {
+|				IF_NOT_ZVAL_REFCOUNTED addr, >4, tmp_reg1, tmp_reg2
+||			}
+||		}
+|		// if (!Z_DELREF_P(cv)) {
+|		GET_ZVAL_PTR FCARG1x, addr, Rx(tmp_reg2)
+|		GC_DELREF FCARG1x, Rw(tmp_reg1)
+||		if (RC_MAY_BE_1(op_info)) {
+||			if (RC_MAY_BE_N(op_info)) {
+||				if (gc && RC_MAY_BE_N(op_info) && ((op_info) & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
+|					bne >3
+||				} else {
+|					bne >4
+||				}
+||			}
+|			// zval_dtor_func(r);
+|			ZVAL_DTOR_FUNC op_info, opline, Rx(tmp_reg1)
+||			if (gc && RC_MAY_BE_N(op_info) && ((op_info) & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
+|				b >4
+||			}
+|3:
+||		}
+||		if (gc && RC_MAY_BE_N(op_info) && ((op_info) & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
+||			if ((op_info) & MAY_BE_REF) {
+||				zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, offsetof(zend_reference, val));
+|				IF_NOT_ZVAL_TYPE addr, IS_REFERENCE, >1, tmp_reg1
+|				IF_NOT_ZVAL_COLLECTABLE ref_addr, >4, tmp_reg1, tmp_reg2
+|				GET_ZVAL_PTR FCARG1x, ref_addr, Rx(tmp_reg2)
+|1:
+||			}
+|			IF_GC_MAY_NOT_LEAK FCARG1x, >4, Rw(tmp_reg1), Rw(tmp_reg2)
+|			// gc_possible_root(Z_COUNTED_P(z))
+|			EXT_CALL gc_possible_root, Rx(tmp_reg1)
+||		}
+||		if (cold && ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) != 0) {
+|			b >4
+|.code
+||		}
+|4:
+||	}
+|.endmacro
+
+|.macro FREE_OP, op_type, op, op_info, cold, opline, tmp_reg1, tmp_reg2
+||	if (op_type & (IS_VAR|IS_TMP_VAR)) {
+||		zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var);
+|		ZVAL_PTR_DTOR addr, op_info, 0, cold, opline, tmp_reg1, tmp_reg2
+||	}
+|.endmacro
+
+|.macro SEPARATE_ARRAY, addr, op_info, cold, tmp_reg1, tmp_reg2
+||	if (RC_MAY_BE_N(op_info)) {
+||		if (Z_REG(addr) != ZREG_FP) {
+|			GET_ZVAL_LVAL ZREG_REG0, addr, Rx(tmp_reg1)
+||			if (RC_MAY_BE_1(op_info)) {
+|				// if (GC_REFCOUNT() > 1)
+|				ldr Rw(tmp_reg1), [REG0]
+|				cmp Rw(tmp_reg1), #1
+|				bls >2
+||			}
+||			if (Z_REG(addr) != ZREG_FCARG1x || Z_OFFSET(addr) != 0) {
+|				LOAD_ZVAL_ADDR FCARG1x, addr
+||			}
+|			EXT_CALL zend_jit_zval_array_dup, REG0
+|			mov REG0, RETVALx
+|2:
+|			mov FCARG1x, REG0
+||		} else {
+|			GET_ZVAL_LVAL ZREG_FCARG1x, addr, Rx(tmp_reg1)
+||			if (RC_MAY_BE_1(op_info)) {
+|				// if (GC_REFCOUNT() > 1)
+|				ldr Rw(tmp_reg1), [FCARG1x]
+|				cmp Rw(tmp_reg1), #1
+||				if (cold) {
+|					bhi >1
+|.cold_code
+|1:
+||				} else {
+|					bls >2
+||				}
+||			}
+|			IF_NOT_ZVAL_REFCOUNTED addr, >1, tmp_reg1, tmp_reg2
+|			GC_DELREF FCARG1x, Rw(tmp_reg1)
+|1:
+|			EXT_CALL zend_array_dup, REG0
+|			mov REG0, RETVALx
+|			SET_ZVAL_PTR addr, REG0, Rx(tmp_reg1)
+|			SET_ZVAL_TYPE_INFO addr, IS_ARRAY_EX, Rw(tmp_reg1), Rx(tmp_reg2)
+|			mov FCARG1x, REG0
+||			if (RC_MAY_BE_1(op_info)) {
+||				if (cold) {
+|					b >2
+|.code
+||				}
+||			}
+|2:
+||		}
+||	} else {
+|		GET_ZVAL_LVAL ZREG_FCARG1x, addr, Rx(tmp_reg1)
+||	}
+|.endmacro
+
+/* argument is passed in FCARG1x */
+|.macro EFREE_REFERENCE
+||#if ZEND_DEBUG
+|		mov FCARG2x, xzr // filename
+|		mov CARG3w, wzr // lineno
+|		mov CARG4, xzr
+|		mov CARG5, xzr
+|		EXT_CALL _efree, REG0
+||#else
+||#ifdef HAVE_BUILTIN_CONSTANT_P
+|		EXT_CALL _efree_32, REG0
+||#else
+|		EXT_CALL _efree, REG0
+||#endif
+||#endif
+|.endmacro
+
+|.macro EMALLOC, size, op_array, opline
+||#if ZEND_DEBUG
+||		const char *filename = op_array->filename ? op_array->filename->val : NULL;
+|		mov FCARG1x, #size
+|		LOAD_ADDR FCARG2x, filename
+|		LOAD_32BIT_VAL CARG3w, opline->lineno
+|		mov CARG4, xzr
+|		mov CARG5, xzr
+|		EXT_CALL _emalloc, REG0
+|		mov REG0, RETVALx
+||#else
+||#ifdef HAVE_BUILTIN_CONSTANT_P
+||	if (size > 24 && size <= 32) {
+|		EXT_CALL _emalloc_32, REG0
+|		mov REG0, RETVALx
+||	} else {
+|		mov FCARG1x, #size
+|		EXT_CALL _emalloc, REG0
+|		mov REG0, RETVALx
+||	}
+||#else
+|		mov FCARG1x, #size
+|		EXT_CALL _emalloc, REG0
+|		mov REG0, RETVALx
+||#endif
+||#endif
+|.endmacro
+
+|.macro OBJ_RELEASE, reg, exit_label, tmp_reg1, tmp_reg2
+|	GC_DELREF Rx(reg), Rw(tmp_reg1)
+|	bne >1
+|	// zend_objects_store_del(obj);
+||	if (reg != ZREG_FCARG1x) {
+|		mov FCARG1x, Rx(reg)
+||	}
+|	EXT_CALL zend_objects_store_del, Rx(tmp_reg1)
+|	b exit_label
+|1:
+|	IF_GC_MAY_NOT_LEAK Rx(reg), >1, Rw(tmp_reg1), Rw(tmp_reg2)
+|	// gc_possible_root(obj)
+||	if (reg != ZREG_FCARG1x) {
+|		mov FCARG1x, Rx(reg)
+||	}
+|	EXT_CALL gc_possible_root, Rx(tmp_reg1)
+|1:
+|.endmacro
+
+|.macro UNDEFINED_OFFSET, opline
+||	if (opline == last_valid_opline) {
+||		zend_jit_use_last_valid_opline();
+|		bl ->undefined_offset_ex
+||	} else {
+|		SET_EX_OPLINE  opline, REG0
+|		bl ->undefined_offset
+||	}
+|.endmacro
+
+|.macro UNDEFINED_INDEX, opline
+||	if (opline == last_valid_opline) {
+||		zend_jit_use_last_valid_opline();
+|		bl ->undefined_index_ex
+||	} else {
+|		SET_EX_OPLINE opline, REG0
+|		bl ->undefined_index
+||	}
+|.endmacro
+
+|.macro CANNOT_ADD_ELEMENT, opline
+||	if (opline == last_valid_opline) {
+||		zend_jit_use_last_valid_opline();
+|		bl ->cannot_add_element_ex
+||	} else {
+|		SET_EX_OPLINE opline, REG0
+|		bl ->cannot_add_element
+||	}
+|.endmacro
+
+static bool reuse_ip = 0;
+static bool delayed_call_chain = 0;
+static uint32_t  delayed_call_level = 0;
+static const zend_op *last_valid_opline = NULL;
+static bool use_last_vald_opline = 0;
+static bool track_last_valid_opline = 0;
+static int jit_return_label = -1;
+static uint32_t current_trace_num = 0;
+static uint32_t allowed_opt_flags = 0;
+
+static void zend_jit_track_last_valid_opline(void)
+{
+	use_last_vald_opline = 0;
+	track_last_valid_opline = 1;
+}
+
+static void zend_jit_use_last_valid_opline(void)
+{
+	if (track_last_valid_opline) {
+		use_last_vald_opline = 1;
+		track_last_valid_opline = 0;
+	}
+}
+
+static bool zend_jit_trace_uses_initial_ip(void)
+{
+	return use_last_vald_opline;
+}
+
+static void zend_jit_set_last_valid_opline(const zend_op *target_opline)
+{
+	if (!reuse_ip) {
+		track_last_valid_opline = 0;
+		last_valid_opline = target_opline;
+	}
+}
+
+static void zend_jit_reset_last_valid_opline(void)
+{
+	track_last_valid_opline = 0;
+	last_valid_opline = NULL;
+}
+
+static void zend_jit_start_reuse_ip(void)
+{
+	zend_jit_reset_last_valid_opline();
+	reuse_ip = 1;
+}
+
+static int zend_jit_reuse_ip(dasm_State **Dst)
+{
+	if (!reuse_ip) {
+		zend_jit_start_reuse_ip();
+		|	// call = EX(call);
+		|	ldr RX, EX->call
+	}
+	return 1;
+}
+
+static void zend_jit_stop_reuse_ip(void)
+{
+	reuse_ip = 0;
+}
+
+static int zend_jit_interrupt_handler_stub(dasm_State **Dst)
+{
+	|->interrupt_handler:
+	|	SAVE_IP
+	|	//EG(vm_interrupt) = 0;
+	|	MEM_STORE_BYTE_ZTS strb, wzr, executor_globals, vm_interrupt, TMP1
+	|	//if (EG(timed_out)) {
+	|	MEM_LOAD_BYTE_ZTS ldrb, REG0w, executor_globals, timed_out, TMP1
+	|	cbz REG0w, >1
+	|	//zend_timeout();
+	|	EXT_CALL zend_timeout, TMP1
+	|1:
+	|	//} else if (zend_interrupt_function) {
+	if (zend_interrupt_function) {
+		|	//zend_interrupt_function(execute_data);
+		|	mov CARG1, FP
+		|	EXT_CALL zend_interrupt_function, TMP1
+		|	//ZEND_VM_ENTER();
+		|	//execute_data = EG(current_execute_data);
+		|	MEM_LOAD_ZTS ldr, FP, executor_globals, current_execute_data, TMP1
+		|	LOAD_IP
+	}
+	|	//ZEND_VM_CONTINUE()
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	ADD_HYBRID_SPAD
+		|	JMP_IP TMP1
+	} else if (GCC_GLOBAL_REGS) {
+		|	ldp x29, x30, [sp], # SPAD // stack alignment
+		|	JMP_IP TMP1
+	} else {
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	mov RETVALx, #1     // ZEND_VM_ENTER
+		|	ret
+	}
+
+	return 1;
+}
+
+static int zend_jit_exception_handler_stub(dasm_State **Dst)
+{
+	|->exception_handler:
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		const void *handler = zend_get_opcode_handler_func(EG(exception_op));
+
+		|	ADD_HYBRID_SPAD
+		|	EXT_CALL handler, REG0
+		|	JMP_IP TMP1
+	} else {
+		const void *handler = EG(exception_op)->handler;
+
+		if (GCC_GLOBAL_REGS) {
+			|	ldp x29, x30, [sp], # SPAD // stack alignment
+			|	EXT_JMP handler, REG0
+		} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			|	mov FCARG1x, FP
+			|	EXT_CALL handler, REG0
+			|	ldp FP, RX, T2                // retore FP and IP
+			|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+			|	tst RETVALw, RETVALw
+			|	blt >1
+			|	mov RETVALw, #1 // ZEND_VM_ENTER
+			|1:
+			|	ret
+		} else {
+			|	mov FCARG1x, FP
+			|	ldp FP, RX, T2                // retore FP and IP
+			|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+			|	EXT_JMP handler, REG0
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_exception_handler_undef_stub(dasm_State **Dst)
+{
+	|->exception_handler_undef:
+	|	MEM_LOAD_ZTS ldr, REG0, executor_globals, opline_before_exception, REG0
+	|	ldrb TMP1w, OP:REG0->result_type
+	|	TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w
+	|	bne >1
+	|	ldr REG0w, OP:REG0->result.var
+	|	add REG0, REG0, FP
+	|	SET_Z_TYPE_INFO REG0, IS_UNDEF, TMP1w
+	|1:
+	|	b ->exception_handler
+
+	return 1;
+}
+
+static int zend_jit_leave_function_stub(dasm_State **Dst)
+{
+	|->leave_function_handler:
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	TST_32_WITH_CONST FCARG1w, ZEND_CALL_TOP, TMP1w
+		|	bne >1
+		|	EXT_CALL zend_jit_leave_nested_func_helper, REG0
+		|	ADD_HYBRID_SPAD
+		|	JMP_IP TMP1
+		|1:
+		|	EXT_CALL zend_jit_leave_top_func_helper, REG0
+		|	ADD_HYBRID_SPAD
+		|	JMP_IP TMP1
+	} else {
+		if (GCC_GLOBAL_REGS) {
+			|	ldp x29, x30, [sp], # SPAD // stack alignment
+		} else {
+			|	mov FCARG2x, FP
+			|	ldp FP, RX, T2                // retore FP and IP
+			|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		}
+		|	TST_32_WITH_CONST FCARG1w, ZEND_CALL_TOP, TMP1w
+		|	bne >1
+		|	EXT_JMP zend_jit_leave_nested_func_helper, REG0
+		|1:
+		|	EXT_JMP zend_jit_leave_top_func_helper, REG0
+	}
+
+	return 1;
+}
+
+static int zend_jit_leave_throw_stub(dasm_State **Dst)
+{
+	|->leave_throw_handler:
+	|	// if (opline->opcode != ZEND_HANDLE_EXCEPTION) {
+	if (GCC_GLOBAL_REGS) {
+		|	ldrb TMP1w, OP:IP->opcode
+		|	cmp TMP1w, #ZEND_HANDLE_EXCEPTION
+		|	beq >5
+		|	// EG(opline_before_exception) = opline;
+		|	MEM_STORE_ZTS str, IP, executor_globals, opline_before_exception, TMP2
+		|5:
+		|	// opline = EG(exception_op);
+		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2
+		|	// HANDLE_EXCEPTION()
+		|	b ->exception_handler
+	} else {
+		|	GET_IP TMP1
+		|	ldrb TMP2w, OP:TMP1->opcode
+		|	cmp TMP2w, #ZEND_HANDLE_EXCEPTION
+		|	beq >5
+		|	// EG(opline_before_exception) = opline;
+		|	MEM_STORE_ZTS str, TMP1, executor_globals, opline_before_exception, TMP2
+		|5:
+		|	// opline = EG(exception_op);
+		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	mov RETVALx, #2     // ZEND_VM_LEAVE
+		|	ret
+	}
+
+	return 1;
+}
+
+static int zend_jit_icall_throw_stub(dasm_State **Dst)
+{
+	|->icall_throw_handler:
+	|	// zend_rethrow_exception(zend_execute_data *execute_data)
+	|	ldr IP, EX->opline
+	|	// if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) {
+	|	ldrb TMP1w, OP:IP->opcode
+	|	cmp TMP1w,# ZEND_HANDLE_EXCEPTION
+	|	beq >1
+	|	// EG(opline_before_exception) = opline;
+	|	MEM_STORE_ZTS str, IP, executor_globals, opline_before_exception, TMP2
+	|1:
+	|	// opline = EG(exception_op);
+	|	LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2
+	||	if (GCC_GLOBAL_REGS) {
+	|		str IP, EX->opline
+	||	}
+	|	// HANDLE_EXCEPTION()
+	|	b ->exception_handler
+
+	return 1;
+}
+
+static int zend_jit_throw_cannot_pass_by_ref_stub(dasm_State **Dst)
+{
+	zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+
+	|->throw_cannot_pass_by_ref:
+	|	ldr REG0, EX->opline
+	|	ldr REG1w, OP:REG0->result.var
+	|	add REG1, REG1, RX
+	|	SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w
+	|	// last EX(call) frame may be delayed
+	|	ldr TMP1, EX->call
+	|	cmp RX, TMP1
+	|	beq >1
+	|	ldr REG1, EX->call
+	|	str REG1, EX:RX->prev_execute_data
+	|	str RX, EX->call
+	|1:
+	|	mov RX, REG0
+	|	ldr FCARG1w, OP:REG0->op2.num
+	|	EXT_CALL zend_cannot_pass_by_reference, REG0
+	|	ldrb TMP1w, OP:RX->op1_type
+	|	cmp TMP1w, #IS_TMP_VAR
+	|	bne >9
+	|	ldr REG0w, OP:RX->op1.var
+	|	add REG0, REG0, FP
+	|	ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2
+	|9:
+	|	b ->exception_handler
+
+	return 1;
+}
+
+static int zend_jit_undefined_offset_ex_stub(dasm_State **Dst)
+{
+	|->undefined_offset_ex:
+	|	SAVE_IP
+	|	b ->undefined_offset
+
+	return 1;
+}
+
+static int zend_jit_undefined_offset_stub(dasm_State **Dst)
+{
+	|->undefined_offset:
+	|	//sub r4, 8
+	|	ldr REG0, EX->opline
+	|	ldr REG1w, OP:REG0->result.var
+	|	add REG1, REG1, FP
+	|	SET_Z_TYPE_INFO REG1, IS_NULL, TMP1w
+	|	ldrb REG1w, OP:REG0->op2_type
+	|	cmp REG1w, #IS_CONST
+	|	bne >2
+	|	ldrsw REG1, OP:REG0->op2.constant
+	|	add REG0, REG0, REG1
+	|	b >3
+	|2:
+	|	ldr REG0w, OP:REG0->op2.var
+	|	add REG0, REG0, FP
+	|3:
+	|	mov CARG1, #E_WARNING
+	|	LOAD_ADDR CARG2, "Undefined array key " ZEND_LONG_FMT
+	|	ldr CARG3, [REG0]
+	|	EXT_JMP zend_error, REG0 // tail call
+	|	//add r4, 8 // stack alignment
+	|	//ret
+
+	return 1;
+}
+
+static int zend_jit_undefined_index_ex_stub(dasm_State **Dst)
+{
+	|->undefined_index_ex:
+	|	SAVE_IP
+	|	b ->undefined_index
+
+	return 1;
+}
+
+static int zend_jit_undefined_index_stub(dasm_State **Dst)
+{
+	|->undefined_index:
+	|	//sub r4, 8
+	|	ldr REG0, EX->opline
+	|	ldr REG1w, OP:REG0->result.var
+	|	add REG1, REG1, FP
+	|	SET_Z_TYPE_INFO REG1, IS_NULL, TMP1w
+	|	ldrb REG1w, OP:REG0->op2_type
+	|	cmp REG1w, #IS_CONST
+	|	bne >2
+	|	ldrsw REG1, OP:REG0->op2.constant
+	|   add REG0, REG0, REG1
+	|	b >3
+	|2:
+	|	ldr REG0w, OP:REG0->op2.var
+	|	add REG0, REG0, FP
+	|3:
+	|	mov CARG1, #E_WARNING
+	|	LOAD_ADDR CARG2, "Undefined array key \"%s\""
+	|	ldr CARG3, [REG0]
+	|	add CARG3, CARG3, #offsetof(zend_string, val)
+	|	EXT_JMP zend_error,REG0 // tail call
+	|	//add r4, 8
+	|	//ret
+
+	return 1;
+}
+
+static int zend_jit_cannot_add_element_ex_stub(dasm_State **Dst)
+{
+	|->cannot_add_element_ex:
+	|	SAVE_IP
+	|	b ->cannot_add_element
+
+	return 1;
+}
+
+static int zend_jit_cannot_add_element_stub(dasm_State **Dst)
+{
+	|->cannot_add_element:
+	|	// sub r4, 8
+	|	ldr REG0, EX->opline
+	|	ldrb TMP1w, OP:REG0->result_type
+	|	cmp TMP1w, #IS_UNUSED
+	|	beq >1
+	|	ldr REG0w, OP:REG0->result.var
+	|	add REG0, REG0, FP
+	|	SET_Z_TYPE_INFO REG0, IS_NULL, TMP1w
+	|1:
+	|	mov CARG1, xzr
+	|	LOAD_ADDR CARG2, "Cannot add element to the array as the next element is already occupied"
+	|	EXT_JMP zend_throw_error, REG0 // tail call
+	|	// add r4, 8
+	|	//ret
+
+	return 1;
+}
+
+static int zend_jit_undefined_function_stub(dasm_State **Dst)
+{
+	|->undefined_function:
+	|	ldr REG0, EX->opline
+	|	mov CARG1, xzr
+	|	LOAD_ADDR CARG2, "Call to undefined function %s()"
+	|	ldrsw CARG3, [REG0, #offsetof(zend_op, op2.constant)]
+	|	ldr CARG3, [REG0, CARG3]
+	|	add CARG3, CARG3, #offsetof(zend_string, val)
+	|	EXT_CALL zend_throw_error, REG0
+	|	b ->exception_handler
+	return 1;
+}
+
+static int zend_jit_negative_shift_stub(dasm_State **Dst)
+{
+	|->negative_shift:
+	|	UNDEF_OPLINE_RESULT TMP1w
+	|	LOAD_ADDR CARG1, zend_ce_arithmetic_error
+	|	LOAD_ADDR CARG2, "Bit shift by negative number"
+	|	EXT_CALL zend_throw_error, REG0
+	|	b ->exception_handler
+	return 1;
+}
+
+static int zend_jit_mod_by_zero_stub(dasm_State **Dst)
+{
+	|->mod_by_zero:
+	|	UNDEF_OPLINE_RESULT TMP1w
+	|	LOAD_ADDR CARG1, zend_ce_division_by_zero_error
+	|	LOAD_ADDR CARG2, "Modulo by zero"
+	|	EXT_CALL zend_throw_error, REG0
+	|	b ->exception_handler
+	return 1;
+}
+
+static int zend_jit_invalid_this_stub(dasm_State **Dst)
+{
+	|->invalid_this:
+	|	UNDEF_OPLINE_RESULT TMP1w
+	|	mov CARG1, xzr
+	|	LOAD_ADDR CARG2, "Using $this when not in object context"
+	|	EXT_CALL zend_throw_error, REG0
+	|	b ->exception_handler
+
+	return 1;
+}
+
+static int zend_jit_hybrid_runtime_jit_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
+		return 1;
+	}
+
+	|->hybrid_runtime_jit:
+	|	EXT_CALL zend_runtime_jit, REG0
+	|	JMP_IP TMP1
+	return 1;
+}
+
+static int zend_jit_hybrid_profile_jit_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
+		return 1;
+	}
+
+	|->hybrid_profile_jit:
+	|	// ++zend_jit_profile_counter;
+	|	LOAD_ADDR REG0, &zend_jit_profile_counter
+	|	ldr TMP1, [REG0]
+	|	add TMP1, TMP1, #1
+	|	str TMP1, [REG0]
+	|	// op_array = (zend_op_array*)EX(func);
+	|	ldr REG0, EX->func
+	|	// run_time_cache = EX(run_time_cache);
+	|	ldr REG2, EX->run_time_cache
+	|	// jit_extension = (const void*)ZEND_FUNC_INFO(op_array);
+	|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+	|	// ++ZEND_COUNTER_INFO(op_array)
+	||	if ((zend_jit_profile_counter_rid * sizeof(void*)) > LDR_STR_PIMM64) {
+	|		LOAD_32BIT_VAL TMP1, (zend_jit_profile_counter_rid * sizeof(void*))
+	|		ldr TMP2, [REG2, TMP1]
+	|		add TMP2, TMP2, #1
+	|		str TMP2, [REG2, TMP1]
+	||	} else {
+	|		ldr TMP2, [REG2, #(zend_jit_profile_counter_rid * sizeof(void*))]
+	|		add TMP2, TMP2, #1
+	|		str TMP2, [REG2, #(zend_jit_profile_counter_rid * sizeof(void*))]
+	||	}
+	|	// return ((zend_vm_opcode_handler_t)jit_extension->orig_handler)()
+	|	ldr TMP1, [REG0, #offsetof(zend_jit_op_array_extension, orig_handler)]
+	|	br TMP1
+	return 1;
+}
+
+static int zend_jit_hybrid_hot_code_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
+		return 1;
+	}
+
+	|->hybrid_hot_code:
+	||	ZEND_ASSERT(ZEND_JIT_COUNTER_INIT <= MOVZ_IMM);
+	|	movz TMP1w, #ZEND_JIT_COUNTER_INIT
+	|	strh TMP1w, [REG2]
+	|	mov FCARG1x, FP
+	|	GET_IP FCARG2x
+	|	EXT_CALL zend_jit_hot_func, REG0
+	|	JMP_IP TMP1
+	return 1;
+}
+
+/*
+ * This code is based Mike Pall's "Hashed profile counters" idea, implemented
+ * in LuaJIT. The full description may be found in "LuaJIT 2.0 intellectual
+ * property disclosure and research opportunities" email
+ * at http://lua-users.org/lists/lua-l/2009-11/msg00089.html
+ *
+ * In addition we use a variation of Knuth's multiplicative hash function
+ * described at https://code.i-harness.com/en/q/a21ce
+ *
+ * uint64_t hash(uint64_t x) {
+ *    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
+ *    x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
+ *    x = x ^ (x >> 31);
+ *    return x;
+ * }
+ *
+ * uint_32_t hash(uint32_t x) {
+ *    x = ((x >> 16) ^ x) * 0x45d9f3b;
+ *    x = ((x >> 16) ^ x) * 0x45d9f3b;
+ *    x = (x >> 16) ^ x;
+ *    return x;
+ * }
+ *
+ */
+static int zend_jit_hybrid_hot_counter_stub(dasm_State **Dst, uint32_t cost)
+{
+	|	ldr REG0, EX->func
+	|	ldr REG1, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+	|	ldr REG2, [REG1, #offsetof(zend_jit_op_array_hot_extension, counter)]
+	|	ldrh TMP2w, [REG2]
+	|	ADD_SUB_32_WITH_CONST subs, TMP2w, TMP2w, cost, TMP1w
+	|	strh TMP2w, [REG2]
+	|	ble ->hybrid_hot_code
+	|	GET_IP REG2
+	|	ldr TMP1, [REG0, #offsetof(zend_op_array, opcodes)]
+	|	sub REG2, REG2, TMP1
+	|	// divide by sizeof(zend_op)
+	||	ZEND_ASSERT(sizeof(zend_op) == 32);
+	|	asr REG2, REG2, #2
+	|	add TMP1, REG1, REG2
+	|	ldr TMP1, [TMP1, #offsetof(zend_jit_op_array_hot_extension, orig_handlers)]
+	|	br TMP1
+	return 1;
+}
+
+static int zend_jit_hybrid_func_hot_counter_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
+		return 1;
+	}
+
+	|->hybrid_func_hot_counter:
+
+	return zend_jit_hybrid_hot_counter_stub(Dst,
+		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func)));
+}
+
+static int zend_jit_hybrid_loop_hot_counter_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
+		return 1;
+	}
+
+	|->hybrid_loop_hot_counter:
+
+	return zend_jit_hybrid_hot_counter_stub(Dst,
+		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
+}
+
+static int zend_jit_hybrid_hot_trace_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
+		return 1;
+	}
+
+	// On entry from counter stub:
+	//   REG2 -> zend_op_trace_info.counter
+
+	|->hybrid_hot_trace:
+	|	mov TMP1w, #ZEND_JIT_COUNTER_INIT
+	|	strh TMP1w, [REG2]
+	|	mov FCARG1x, FP
+	|	GET_IP FCARG2x
+	|	EXT_CALL zend_jit_trace_hot_root, REG0
+	|	cmp RETVALw, wzr   // Result is < 0 on failure.
+	|	blt >1
+	|	MEM_LOAD_ZTS ldr, FP, executor_globals, current_execute_data, REG0
+	|	LOAD_IP
+	|	JMP_IP TMP1
+	|1:
+	|	EXT_JMP zend_jit_halt_op->handler, REG0
+
+	return 1;
+}
+
+static int zend_jit_hybrid_trace_counter_stub(dasm_State **Dst, uint32_t cost)
+{
+	|	ldr REG0, EX->func
+	|	ldr REG1, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+	|	ldr REG1, [REG1, #offsetof(zend_jit_op_array_trace_extension, offset)]
+	|	add TMP1, REG1, IP
+	|	ldr REG2, [TMP1, #offsetof(zend_op_trace_info, counter)]
+	|	ldrh TMP2w, [REG2]
+	|	ADD_SUB_32_WITH_CONST subs, TMP2w, TMP2w, cost, TMP1w
+	|	strh TMP2w, [REG2]
+	|	ble ->hybrid_hot_trace
+	// Note: "REG1 + IP" is re-calculated as TMP1 is used as temporary register by the prior
+	// ADD_SUB_32_WITH_CONST. Will optimize in the future if more temporary registers are available.
+	|	add TMP1, REG1, IP
+	|	ldr TMP2, [TMP1, #offsetof(zend_op_trace_info, orig_handler)]
+	|	br TMP2
+
+	return 1;
+}
+
+static int zend_jit_hybrid_func_trace_counter_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
+		return 1;
+	}
+
+	|->hybrid_func_trace_counter:
+
+	return zend_jit_hybrid_trace_counter_stub(Dst,
+		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1)  / JIT_G(hot_func)));
+}
+
+static int zend_jit_hybrid_ret_trace_counter_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_return)) {
+		return 1;
+	}
+
+	|->hybrid_ret_trace_counter:
+
+	return zend_jit_hybrid_trace_counter_stub(Dst,
+		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_return) - 1) / JIT_G(hot_return)));
+}
+
+static int zend_jit_hybrid_loop_trace_counter_stub(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
+		return 1;
+	}
+
+	|->hybrid_loop_trace_counter:
+
+	return zend_jit_hybrid_trace_counter_stub(Dst,
+		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
+}
+
+static int zend_jit_trace_halt_stub(dasm_State **Dst)
+{
+	|->trace_halt:
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	ADD_HYBRID_SPAD
+		|	EXT_JMP zend_jit_halt_op->handler, REG0
+	} else if (GCC_GLOBAL_REGS) {
+		|	ldp x29, x30, [sp], # SPAD // stack alignment
+		|	ret // PC must be zero
+	} else {
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	movn RETVALx, #0              // ZEND_VM_RETURN (-1)
+		|	ret
+	}
+	return 1;
+}
+
+static int zend_jit_trace_exit_stub(dasm_State **Dst)
+{
+	|->trace_exit:
+	|
+	|	// Save CPU registers(32 GP regs + 32 FP regs) on stack in the order of d31 to x0
+	|
+	|	stp d30, d31, [sp, #-16]!
+	|	stp d28, d29, [sp, #-16]!
+	|	stp d26, d27, [sp, #-16]!
+	|	stp d24, d25, [sp, #-16]!
+	|	stp d22, d23, [sp, #-16]!
+	|	stp d20, d21, [sp, #-16]!
+	|	stp d18, d19, [sp, #-16]!
+	|	stp d16, d17, [sp, #-16]!
+	|	//stp d14, d15, [sp, #-16]!     // we don't use preserved registers yet
+	|	//stp d12, d13, [sp, #-16]!
+	|	//stp d10, d11, [sp, #-16]!
+	|	//stp d8, d9, [sp, #-16]!
+	|	stp d6, d7, [sp, #(-16*5)]!
+	|	stp d4, d5, [sp, #-16]!
+	|	stp d2, d3, [sp, #-16]!
+	|	stp d0, d1, [sp, #-16]!
+	|
+	|	//str x30, [sp, #-16]!          // we don't use callee-saved registers yet (x31 can be omitted)
+	|	stp x28, x29, [sp, #(-16*2)]!   // we have to store RX (x28)
+	|	//stp x26, x27, [sp, #-16]!     // we don't use callee-saved registers yet
+	|	//stp x24, x25, [sp, #-16]!
+	|	//stp x22, x23, [sp, #-16]!
+	|	//stp x20, x21, [sp, #-16]!
+	|	//stp x18, x19, [sp, #-16]!
+	|	//stp x16, x17, [sp, #-16]!     // we don't need temporary registers
+	|	stp x14, x15, [sp, #-(16*7)]!
+	|	stp x12, x13, [sp, #-16]!
+	|	stp x10, x11, [sp, #-16]!
+	|	stp x8, x9, [sp, #-16]!
+	|	stp x6, x7, [sp, #-16]!
+	|	stp x4, x5, [sp, #-16]!
+	|	stp x2, x3, [sp, #-16]!
+	|	stp x0, x1, [sp, #-16]!
+	|
+	|	mov FCARG1w, TMP1w              // exit_num
+	|	mov FCARG2x, sp
+	|
+	| 	// EX(opline) = opline
+	|	SAVE_IP
+	|	// zend_jit_trace_exit(trace_num, exit_num)
+	|	EXT_CALL zend_jit_trace_exit, REG0
+	|
+	|	add sp, sp, #(32 * 16)          // restore sp
+	|
+
+	|	tst RETVALw, RETVALw
+	|	bne >1  // not zero
+
+	|	// execute_data = EG(current_execute_data)
+	|	MEM_LOAD_ZTS ldr, FP, executor_globals, current_execute_data, REG0
+	|	// opline = EX(opline)
+	|	LOAD_IP
+
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	ADD_HYBRID_SPAD
+		|	JMP_IP TMP1
+	} else if (GCC_GLOBAL_REGS) {
+		|	ldp x29, x30, [sp], # SPAD // stack alignment
+		|	JMP_IP TMP1
+	} else {
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	mov RETVALx, #1     // ZEND_VM_ENTER
+		|	ret
+	}
+
+	|1:
+	|	blt ->trace_halt
+
+	|	// execute_data = EG(current_execute_data)
+	|	MEM_LOAD_ZTS ldr, FP, executor_globals, current_execute_data, REG0
+	|	// opline = EX(opline)
+	|	LOAD_IP
+
+	|	// check for interrupt (try to avoid this ???)
+	|	MEM_LOAD_BYTE_ZTS ldrb, REG0w, executor_globals, vm_interrupt, TMP1
+	|	cbnz REG0w, ->interrupt_handler
+
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	ADD_HYBRID_SPAD
+		|	ldr REG0, EX->func
+		|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+		|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
+		|	add REG0, IP, REG0
+		|	ldr REG0, [REG0]
+		|	br REG0
+	} else if (GCC_GLOBAL_REGS) {
+		|	ldp x29, x30, [sp], # SPAD // stack alignment
+		|	ldr REG0, EX->func
+		|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+		|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
+		|	add REG0, IP, REG0
+		|	ldr REG0, [REG0]
+		|	br REG0
+	} else {
+		|	ldr IP, EX->opline
+		|	mov FCARG1x, FP
+		|	ldr REG0, EX->func
+		|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+		|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
+		|	add REG0, IP, REG0
+		|	ldr REG0, [REG0]
+		|	blr REG0
+		|
+		|	tst RETVALw, RETVALw
+		|	blt ->trace_halt
+		|
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	mov RETVALx, #1               // ZEND_VM_ENTER
+		|	ret
+	}
+
+	return 1;
+}
+
+static int zend_jit_trace_escape_stub(dasm_State **Dst)
+{
+	|->trace_escape:
+	|
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	ADD_HYBRID_SPAD
+		|	JMP_IP, TMP1
+	} else if (GCC_GLOBAL_REGS) {
+		|	ldp x29, x30, [sp], # SPAD // stack alignment
+		|	JMP_IP, TMP1
+	} else {
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	mov RETVALx, #1     // ZEND_VM_ENTER
+		|	ret
+	}
+
+	return 1;
+}
+
+/* Keep 32 exit points in a single code block */
+#define ZEND_JIT_EXIT_POINTS_SPACING    4 // bl = bytes
+#define ZEND_JIT_EXIT_POINTS_PER_GROUP 32 // number of continuous exit points
+
+static int zend_jit_trace_exit_group_stub(dasm_State **Dst, uint32_t n)
+{
+	uint32_t i;
+
+	|	bl >2
+	|1:
+	for (i = 1; i < ZEND_JIT_EXIT_POINTS_PER_GROUP; i++) {
+		|	bl >2
+	}
+	|2:
+	|	adr TMP1, <1
+	|	sub TMP1, lr, TMP1
+	|	lsr TMP1, TMP1, #2
+	if (n) {
+		|	ADD_SUB_32_WITH_CONST add, TMP1w, TMP1w, n, TMP2w
+	}
+	|	b ->trace_exit // pass exit_num in TMP1w
+
+	return 1;
+}
+
+#ifdef CONTEXT_THREADED_JIT
+static int zend_jit_context_threaded_call_stub(dasm_State **Dst)
+{
+	|->context_threaded_call:
+	|	NIY_STUB	// TODO
+	return 1;
+}
+#endif
+
+static int zend_jit_assign_to_variable(dasm_State    **Dst,
+                                       const zend_op  *opline,
+                                       zend_jit_addr   var_use_addr,
+                                       zend_jit_addr   var_addr,
+                                       uint32_t        var_info,
+                                       uint32_t        var_def_info,
+                                       zend_uchar      val_type,
+                                       zend_jit_addr   val_addr,
+                                       uint32_t        val_info,
+                                       zend_jit_addr   res_addr,
+                                       bool       check_exception);
+
+static int zend_jit_assign_const_stub(dasm_State **Dst)
+{
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2x, 0);
+	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
+
+	|->assign_const:
+	|	stp x29, x30, [sp,#-32]!
+	|	mov x29, sp
+	if (!zend_jit_assign_to_variable(
+			Dst, NULL,
+			var_addr, var_addr, -1, -1,
+			IS_CONST, val_addr, val_info,
+			0, 0)) {
+		return 0;
+	}
+	|	ldp x29, x30, [sp],#32
+	|	ret
+	return 1;
+}
+
+static int zend_jit_assign_tmp_stub(dasm_State **Dst)
+{
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2x, 0);
+	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
+
+	|->assign_tmp:
+	|	stp x29, x30, [sp,#-32]!
+	|	mov x29, sp
+	if (!zend_jit_assign_to_variable(
+			Dst, NULL,
+			var_addr, var_addr, -1, -1,
+			IS_TMP_VAR, val_addr, val_info,
+			0, 0)) {
+		return 0;
+	}
+	|	ldp x29, x30, [sp],#32
+	|	ret
+	return 1;
+}
+
+static int zend_jit_assign_var_stub(dasm_State **Dst)
+{
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2x, 0);
+	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF;
+
+	|->assign_var:
+	|	stp x29, x30, [sp,#-32]!
+	|	mov x29, sp
+	if (!zend_jit_assign_to_variable(
+			Dst, NULL,
+			var_addr, var_addr, -1, -1,
+			IS_VAR, val_addr, val_info,
+			0, 0)) {
+		return 0;
+	}
+	|	ldp x29, x30, [sp],#32
+	|	ret
+	return 1;
+}
+
+static int zend_jit_assign_cv_noref_stub(dasm_State **Dst)
+{
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2x, 0);
+	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN/*|MAY_BE_UNDEF*/;
+
+	|->assign_cv_noref:
+	|	stp x29, x30, [sp,#-32]!
+	|	mov x29, sp
+	if (!zend_jit_assign_to_variable(
+			Dst, NULL,
+			var_addr, var_addr, -1, -1,
+			IS_CV, val_addr, val_info,
+			0, 0)) {
+		return 0;
+	}
+	|	ldp x29, x30, [sp],#32
+	|	ret
+	return 1;
+}
+
+static int zend_jit_assign_cv_stub(dasm_State **Dst)
+{
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2x, 0);
+	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF/*|MAY_BE_UNDEF*/;
+
+	|->assign_cv:
+	|	stp x29, x30, [sp,#-32]!
+	|	mov x29, sp
+	if (!zend_jit_assign_to_variable(
+			Dst, NULL,
+			var_addr, var_addr, -1, -1,
+			IS_CV, val_addr, val_info,
+			0, 0)) {
+		return 0;
+	}
+	|	ldp x29, x30, [sp],#32
+	|	ret
+	return 1;
+}
+
+static const zend_jit_stub zend_jit_stubs[] = {
+	JIT_STUB(interrupt_handler),
+	JIT_STUB(exception_handler),
+	JIT_STUB(exception_handler_undef),
+	JIT_STUB(leave_function),
+	JIT_STUB(leave_throw),
+	JIT_STUB(icall_throw),
+	JIT_STUB(throw_cannot_pass_by_ref),
+	JIT_STUB(undefined_offset),
+	JIT_STUB(undefined_index),
+	JIT_STUB(cannot_add_element),
+	JIT_STUB(undefined_offset_ex),
+	JIT_STUB(undefined_index_ex),
+	JIT_STUB(cannot_add_element_ex),
+	JIT_STUB(undefined_function),
+	JIT_STUB(negative_shift),
+	JIT_STUB(mod_by_zero),
+	JIT_STUB(invalid_this),
+	JIT_STUB(trace_halt),
+	JIT_STUB(trace_exit),
+	JIT_STUB(trace_escape),
+	JIT_STUB(hybrid_runtime_jit),
+	JIT_STUB(hybrid_profile_jit),
+	JIT_STUB(hybrid_hot_code),
+	JIT_STUB(hybrid_func_hot_counter),
+	JIT_STUB(hybrid_loop_hot_counter),
+	JIT_STUB(hybrid_hot_trace),
+	JIT_STUB(hybrid_func_trace_counter),
+	JIT_STUB(hybrid_ret_trace_counter),
+	JIT_STUB(hybrid_loop_trace_counter),
+	JIT_STUB(assign_const),
+	JIT_STUB(assign_tmp),
+	JIT_STUB(assign_var),
+	JIT_STUB(assign_cv_noref),
+	JIT_STUB(assign_cv),
+#ifdef CONTEXT_THREADED_JIT
+	JIT_STUB(context_threaded_call),
+#endif
+};
+
+#if ZTS && defined(ZEND_WIN32)
+extern uint32_t _tls_index;
+extern char *_tls_start;
+extern char *_tls_end;
+#endif
+
+static int zend_jit_setup(void)
+{
+	allowed_opt_flags = 0;
+
+#if ZTS
+# ifdef _WIN64
+	tsrm_tls_index  = _tls_index * sizeof(void*);
+
+	/* To find offset of "_tsrm_ls_cache" in TLS segment we perform a linear scan of local TLS memory */
+	/* Probably, it might be better solution */
+	do {
+		void ***tls_mem = ((void**)__readgsqword(0x58))[_tls_index];
+		void *val = _tsrm_ls_cache;
+		size_t offset = 0;
+		size_t size = (char*)&_tls_end - (char*)&_tls_start;
+
+		while (offset < size) {
+			if (*tls_mem == val) {
+				tsrm_tls_offset = offset;
+				break;
+			}
+			tls_mem++;
+			offset += sizeof(void*);
+		}
+		if (offset >= size) {
+			// TODO: error message ???
+			return FAILURE;
+		}
+	} while(0);
+# elif ZEND_WIN32
+	tsrm_tls_index  = _tls_index * sizeof(void*);
+
+	/* To find offset of "_tsrm_ls_cache" in TLS segment we perform a linear scan of local TLS memory */
+	/* Probably, it might be better solution */
+	do {
+		void ***tls_mem = ((void***)__readfsdword(0x2c))[_tls_index];
+		void *val = _tsrm_ls_cache;
+		size_t offset = 0;
+		size_t size = (char*)&_tls_end - (char*)&_tls_start;
+
+		while (offset < size) {
+			if (*tls_mem == val) {
+				tsrm_tls_offset = offset;
+				break;
+			}
+			tls_mem++;
+			offset += sizeof(void*);
+		}
+		if (offset >= size) {
+			// TODO: error message ???
+			return FAILURE;
+		}
+	} while(0);
+# elif defined(__APPLE__) && defined(__x86_64__)
+	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
+	if (tsrm_ls_cache_tcb_offset == 0) {
+		size_t *ti;
+		__asm__(
+			"leaq __tsrm_ls_cache(%%rip),%0"
+			: "=r" (ti));
+		tsrm_tls_offset = ti[2];
+		tsrm_tls_index = ti[1] * 8;
+	}
+# elif defined(__GNUC__) && defined(__x86_64__)
+	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
+	if (tsrm_ls_cache_tcb_offset == 0) {
+#if defined(__has_attribute) && __has_attribute(tls_model) && !defined(__FreeBSD__)
+		size_t ret;
+
+		asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0"
+			: "=r" (ret));
+		tsrm_ls_cache_tcb_offset = ret;
+#else
+		size_t *ti;
+
+		__asm__(
+			"leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n"
+			: "=a" (ti));
+		tsrm_tls_offset = ti[1];
+		tsrm_tls_index = ti[0] * 16;
+#endif
+	}
+# elif defined(__GNUC__) && defined(__i386__)
+	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
+	if (tsrm_ls_cache_tcb_offset == 0) {
+#if !defined(__FreeBSD__)
+		size_t ret;
+
+		asm ("leal _tsrm_ls_cache@ntpoff,%0\n"
+			: "=a" (ret));
+		tsrm_ls_cache_tcb_offset = ret;
+#else
+		size_t *ti, _ebx, _ecx, _edx;
+
+		__asm__(
+			"call 1f\n"
+			".subsection 1\n"
+			"1:\tmovl (%%esp), %%ebx\n\t"
+			"ret\n"
+			".previous\n\t"
+			"addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t"
+			"leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n\t"
+			"call ___tls_get_addr@plt\n\t"
+			"leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n"
+			: "=a" (ti), "=&b" (_ebx), "=&c" (_ecx), "=&d" (_edx));
+		tsrm_tls_offset = ti[1];
+		tsrm_tls_index = ti[0] * 8;
+#endif
+	}
+# elif defined(__aarch64__)
+	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
+	ZEND_ASSERT(tsrm_ls_cache_tcb_offset != 0);
+# elif
+# endif
+#endif
+
+	return SUCCESS;
+}
+
+static ZEND_ATTRIBUTE_UNUSED int zend_jit_trap(dasm_State **Dst)
+{
+	|	brk #0
+	return 1;
+}
+
+static int zend_jit_align_func(dasm_State **Dst)
+{
+	reuse_ip = 0;
+	delayed_call_chain = 0;
+	last_valid_opline = NULL;
+	use_last_vald_opline = 0;
+	track_last_valid_opline = 0;
+	jit_return_label = -1;
+	|.align 16
+	return 1;
+}
+
+static int zend_jit_prologue(dasm_State **Dst)
+{
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	SUB_HYBRID_SPAD
+	} else if (GCC_GLOBAL_REGS) {
+		|	stp x29, x30, [sp, # -SPAD]!    // stack alignment
+	} else {
+		|	stp x29, x30, [sp, # -NR_SPAD]! // stack alignment
+		|	stp FP, RX, T2                  // save FP and IP
+		|	mov FP, FCARG1x
+	}
+	return 1;
+}
+
+static int zend_jit_label(dasm_State **Dst, unsigned int label)
+{
+	|=>label:
+	return 1;
+}
+
+static int zend_jit_save_call_chain(dasm_State **Dst, uint32_t call_level)
+{
+	|	// call->prev_execute_data = EX(call);
+	if (call_level == 1) {
+		|	str xzr, EX:RX->prev_execute_data
+	} else {
+		|	ldr REG0, EX->call
+		|	str REG0, EX:RX->prev_execute_data
+	}
+	|	// EX(call) = call;
+	|	str RX, EX->call
+
+	delayed_call_chain = 0;
+
+	return 1;
+}
+
+static int zend_jit_set_ip(dasm_State **Dst, const zend_op *opline)
+{
+	if (last_valid_opline == opline) {
+		zend_jit_use_last_valid_opline();
+	} else if (GCC_GLOBAL_REGS && last_valid_opline) {
+		zend_jit_use_last_valid_opline();
+		|	LOAD_64BIT_VAL TMP1, (opline - last_valid_opline) * sizeof(zend_op)
+		|	ADD_IP TMP1, TMP2
+	} else {
+		|	LOAD_IP_ADDR opline
+	}
+	zend_jit_set_last_valid_opline(opline);
+
+	return 1;
+}
+
+static int zend_jit_set_valid_ip(dasm_State **Dst, const zend_op *opline)
+{
+	if (delayed_call_chain) {
+		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
+			return 0;
+		}
+	}
+	if (!zend_jit_set_ip(Dst, opline)) {
+		return 0;
+	}
+	reuse_ip = 0;
+	return 1;
+}
+
+static int zend_jit_check_timeout(dasm_State **Dst, const zend_op *opline, const void *exit_addr)
+{
+	|	MEM_LOAD_BYTE_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1
+	if (exit_addr) {
+		|	cbnz TMP1w, &exit_addr
+	} else if (last_valid_opline == opline) {
+		||		zend_jit_use_last_valid_opline();
+		|	cbnz TMP1w, ->interrupt_handler
+	} else {
+		|	cbnz TMP1w, >1
+		|.cold_code
+		|1:
+		|	LOAD_IP_ADDR opline
+		|	b ->interrupt_handler
+		|.code
+	}
+	return 1;
+}
+
+static int zend_jit_trace_end_loop(dasm_State **Dst, int loop_label, const void *timeout_exit_addr)
+{
+	if (timeout_exit_addr) {
+		|	MEM_LOAD_BYTE_ZTS ldrb, REG0w, executor_globals, vm_interrupt, TMP1
+		|	cbz REG0w, =>loop_label
+		|	b &timeout_exit_addr
+	} else {
+		|	b =>loop_label
+	}
+	return 1;
+}
+
+static int zend_jit_check_exception(dasm_State **Dst)
+{
+	|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+	|	cbnz REG0, ->exception_handler
+	return 1;
+}
+
+static int zend_jit_check_exception_undef_result(dasm_State **Dst, const zend_op *opline)
+{
+	if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
+		|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+		|	cbnz REG0, ->exception_handler_undef
+		return 1;
+	}
+	return zend_jit_check_exception(Dst);
+}
+
+static int zend_jit_trace_begin(dasm_State **Dst, uint32_t trace_num, zend_jit_trace_info *parent, uint32_t exit_num)
+{
+	zend_regset regset = ZEND_REGSET_SCRATCH;
+
+	// In the x86 implementation, this clause would be conducted if ZTS is enabled or the addressing mode is 64-bit.
+	{
+		/* assignment to EG(jit_trace_num) shouldn't clober CPU register used by deoptimizer */
+		if (parent) {
+			int i;
+			int parent_vars_count = parent->exit_info[exit_num].stack_size;
+			zend_jit_trace_stack *parent_stack =
+				parent->stack_map +
+				parent->exit_info[exit_num].stack_offset;
+
+			for (i = 0; i < parent_vars_count; i++) {
+				if (STACK_REG(parent_stack, i) != ZREG_NONE) {
+					if (STACK_REG(parent_stack, i) < ZREG_NUM) {
+						ZEND_REGSET_EXCL(regset, STACK_REG(parent_stack, i));
+					} else if (STACK_REG(parent_stack, i) == ZREG_ZVAL_COPY_GPR0) {
+						ZEND_REGSET_EXCL(regset, ZREG_REG0);
+					}
+				}
+			}
+		}
+	}
+
+	if (parent && parent->exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL) {
+		ZEND_REGSET_EXCL(regset, ZREG_REG0);
+	}
+
+	current_trace_num = trace_num;
+
+	|	// EG(jit_trace_num) = trace_num;
+	if (regset == ZEND_REGSET_EMPTY || ZEND_REGSET_IS_SINGLETON(regset)) {
+		|	sub sp, sp, #16
+		|	stp TMP1, TMP2, [sp]       // save TMP1 and TMP2
+		|	LOAD_32BIT_VAL TMP1w, trace_num
+		|	MEM_STORE_32_ZTS str, TMP1w, executor_globals, jit_trace_num, TMP2
+		|	ldp TMP1, TMP2, [sp]      // retore TMP1 and TMP2
+		|	add sp, sp, #16
+	} else {
+		zend_reg tmp1 = ZEND_REGSET_FIRST(regset);
+		zend_reg tmp2 = ZEND_REGSET_FIRST(ZEND_REGSET_EXCL(regset, tmp1));
+
+		|	LOAD_32BIT_VAL Rw(tmp1), trace_num
+		|	MEM_STORE_32_ZTS str, Rw(tmp1), executor_globals, jit_trace_num, Rx(tmp2)
+		(void)tmp1;
+		(void)tmp2;
+	}
+
+	return 1;
+}
+
+static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size, const void *from_addr, const void *to_addr)
+{
+	int ret = 0;
+	uint8_t *p, *end;
+	size_t code_size = size;
+	ptrdiff_t delta;
+
+	if (jmp_table_size) {
+		const void **jmp_slot = (const void **)((char*)code + size);
+
+		code_size -= jmp_table_size * sizeof(void*);
+		do {
+			jmp_slot--;
+			if (*jmp_slot == from_addr) {
+				*jmp_slot = to_addr;
+				ret++;
+			}
+		} while (--jmp_table_size);
+	}
+
+	p = (uint8_t*)code;
+	end = p + code_size;
+	while (p < end) {
+		uint32_t *ins_ptr = (uint32_t*)p;
+		uint32_t ins = *ins_ptr;
+		if ((ins & 0xfc000000u) == 0x14000000u) {
+			// B (imm26:0..25)
+			delta = (uint32_t*)from_addr - ins_ptr;
+			if (((ins ^ (uint32_t)delta) & 0x01ffffffu) == 0) {
+				delta = (uint32_t*)to_addr - ins_ptr;
+				if (((delta + 0x02000000) >> 26) != 0) {
+					abort(); // brnach target out of range
+				}
+				*ins_ptr = (ins & 0xfc000000u) | ((uint32_t)delta & 0x03ffffffu);
+				ret++;
+			}
+		} else if ((ins & 0xff000000u) == 0x54000000u ||
+		           (ins & 0x7e000000u) == 0x34000000u) {
+			// B.cond, CBZ, CBNZ (imm19:5..23)
+			delta = (uint32_t*)from_addr - ins_ptr;
+			if (((ins ^ ((uint32_t)delta << 5)) & 0x00ffffe0u) == 0) {
+				delta = (uint32_t*)to_addr - ins_ptr;
+				if (((delta + 0x40000) >> 19) != 0) {
+					abort(); // brnach target out of range
+				}
+				*ins_ptr = (ins & 0xff00001fu) | (((uint32_t)delta & 0x7ffffu) << 5);
+				ret++;
+			}
+		} else if ((ins & 0x7e000000u) == 0x36000000u) {
+			// TBZ, TBNZ (imm14:5..18)
+			delta = (uint32_t*)from_addr - ins_ptr;
+			if (((ins ^ ((uint32_t)delta << 5)) & 0x0007ffe0u) == 0) {
+				delta = (uint32_t*)to_addr - ins_ptr;
+				if (((delta + 0x2000) >> 14) != 0) {
+					abort(); // brnach target out of range
+				}
+				*ins_ptr = (ins & 0xfff8001fu) | (((uint32_t)delta & 0x3fffu) << 5);
+				ret++;
+			}
+		}
+		p += 4;
+	}
+
+	JIT_CACHE_FLUSH(code, (char*)code + size);
+
+#ifdef HAVE_VALGRIND
+	VALGRIND_DISCARD_TRANSLATIONS(code, size);
+#endif
+
+	return ret;
+}
+
+static int zend_jit_link_side_trace(const void *code, size_t size, uint32_t jmp_table_size, uint32_t exit_num, const void *addr)
+{
+	return zend_jit_patch(code, size, jmp_table_size, zend_jit_trace_get_exit_addr(exit_num), addr);
+}
+
+static int zend_jit_trace_link_to_root(dasm_State **Dst, zend_jit_trace_info *t, const void *timeout_exit_addr)
+{
+	const void *link_addr;
+	size_t prologue_size;
+
+	/* Skip prologue. */
+	// TODO: don't hardcode this ???
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
+		prologue_size = 0;
+#else
+		// sub sp, sp, #0x20
+		prologue_size = 4;
+#endif
+	} else if (GCC_GLOBAL_REGS) {
+		// stp x29, x30, [sp, # -SPAD]!
+		prologue_size = 4;
+	} else {
+		// stp x29, x30, [sp, # -NR_SPAD]! // stack alignment
+		// stp FP, RX, T2
+		// mov FP, FCARG1x
+		prologue_size = 12;
+	}
+	link_addr = (const void*)((const char*)t->code_start + prologue_size);
+
+	if (timeout_exit_addr) {
+		/* Check timeout for links to LOOP */
+		|	MEM_LOAD_BYTE_ZTS ldrb, REG0w, executor_globals, vm_interrupt, TMP1
+		|	cbz REG0w, &link_addr
+		|	b &timeout_exit_addr
+	} else {
+		|	b &link_addr
+	}
+	return 1;
+}
+
+static int zend_jit_trace_return(dasm_State **Dst, bool original_handler)
+{
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	ADD_HYBRID_SPAD
+		if (!original_handler) {
+			|	JMP_IP TMP1
+		} else {
+			|	ldr REG0, EX->func
+			|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+			|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
+			|	add REG0, IP, REG0
+			|	ldr REG0, [REG0]
+			|	br REG0
+		}
+	} else if (GCC_GLOBAL_REGS) {
+		|	ldp x29, x30, [sp], # SPAD // stack alignment
+		if (!original_handler) {
+			|	JMP_IP TMP1
+		} else {
+			|	ldr REG0, EX->func
+			|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+			|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
+			|	add REG0, IP, REG0
+			|	ldr REG0, [REG0]
+			|	br REG0
+		}
+	} else {
+		if (original_handler) {
+			|	mov FCARG1x, FP
+			|	ldr REG0, EX->func
+			|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
+			|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
+			|	add REG0, IP, REG0
+			|	ldr REG0, [REG0]
+			|	blr REG0
+		}
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	mov RETVALx, #2               // ZEND_VM_LEAVE
+		|	ret
+	}
+	return 1;
+}
+
+static int zend_jit_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint8_t type)
+{
+	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
+	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
+
+	if (!exit_addr) {
+		return 0;
+	}
+
+	|	IF_NOT_ZVAL_TYPE var_addr, type, &exit_addr, ZREG_TMP1
+
+	return 1;
+}
+
+static int zend_jit_packed_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint32_t op_info)
+{
+	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
+	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
+
+	if (!exit_addr) {
+		return 0;
+	}
+
+	|	GET_ZVAL_LVAL ZREG_FCARG1x, var_addr, TMP1
+	if (op_info & MAY_BE_ARRAY_PACKED) {
+		|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
+		|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
+		|	beq &exit_addr
+	} else {
+		|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
+		|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
+		|	bne &exit_addr
+	}
+
+	return 1;
+}
+
+static int zend_jit_trace_handler(dasm_State **Dst, const zend_op_array *op_array, const zend_op *opline, int may_throw, zend_jit_trace_rec *trace)
+{
+	zend_jit_op_array_trace_extension *jit_extension =
+		(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
+	size_t offset = jit_extension->offset;
+	const void *handler =
+		(zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler;
+
+	if (!zend_jit_set_valid_ip(Dst, opline)) {
+		return 0;
+	}
+	if (!GCC_GLOBAL_REGS) {
+		|	mov FCARG1x, FP
+	}
+	|	EXT_CALL handler, REG0
+	if (may_throw
+	 && opline->opcode != ZEND_RETURN
+	 && opline->opcode != ZEND_RETURN_BY_REF) {
+		|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+		|	cbnz REG0, ->exception_handler
+	}
+
+	while (trace->op != ZEND_JIT_TRACE_VM && trace->op != ZEND_JIT_TRACE_END) {
+		trace++;
+	}
+
+	if (!GCC_GLOBAL_REGS
+	 && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_RETURN)) {
+		if (opline->opcode == ZEND_RETURN ||
+		    opline->opcode == ZEND_RETURN_BY_REF ||
+		    opline->opcode == ZEND_DO_UCALL ||
+		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
+		    opline->opcode == ZEND_DO_FCALL ||
+		    opline->opcode == ZEND_GENERATOR_CREATE) {
+			|	MEM_LOAD_ZTS ldr, FP, executor_globals, current_execute_data, TMP1
+		}
+	}
+
+	if (zend_jit_trace_may_exit(op_array, opline)) {
+		if (opline->opcode == ZEND_RETURN ||
+		    opline->opcode == ZEND_RETURN_BY_REF ||
+		    opline->opcode == ZEND_GENERATOR_CREATE) {
+
+			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+#if 0
+				/* this check should be handled by the following OPLINE guard or jmp [IP] */
+				|	LOAD_ADDR TMP1, zend_jit_halt_op
+				|	cmp IP, TMP1
+				|	beq ->trace_halt
+#endif
+			} else if (GCC_GLOBAL_REGS) {
+				|	cbz IP, ->trace_halt
+			} else {
+				|	tst RETVALw, RETVALw
+				|	blt ->trace_halt
+			}
+		} else if (opline->opcode == ZEND_EXIT ||
+		           opline->opcode == ZEND_GENERATOR_RETURN ||
+		           opline->opcode == ZEND_YIELD ||
+		           opline->opcode == ZEND_YIELD_FROM) {
+			|	b ->trace_halt
+		}
+		if (trace->op != ZEND_JIT_TRACE_END ||
+		    (trace->stop != ZEND_JIT_TRACE_STOP_RETURN &&
+		     trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
+
+			const zend_op *next_opline = trace->opline;
+			const zend_op *exit_opline = NULL;
+			uint32_t exit_point;
+			const void *exit_addr;
+			uint32_t old_info = 0;
+			uint32_t old_res_info = 0;
+			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
+
+			if (zend_is_smart_branch(opline)) {
+				bool exit_if_true = 0;
+				exit_opline = zend_jit_trace_get_exit_opline(trace, opline + 1, &exit_if_true);
+			} else {
+				switch (opline->opcode) {
+					case ZEND_JMPZ:
+					case ZEND_JMPNZ:
+					case ZEND_JMPZ_EX:
+					case ZEND_JMPNZ_EX:
+					case ZEND_JMP_SET:
+					case ZEND_COALESCE:
+					case ZEND_JMP_NULL:
+					case ZEND_FE_RESET_R:
+					case ZEND_FE_RESET_RW:
+						exit_opline = (trace->opline == opline + 1) ?
+							OP_JMP_ADDR(opline, opline->op2) :
+							opline + 1;
+						break;
+					case ZEND_JMPZNZ:
+						exit_opline = (trace->opline == OP_JMP_ADDR(opline, opline->op2)) ?
+							ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
+							OP_JMP_ADDR(opline, opline->op2);
+						break;
+					case ZEND_FE_FETCH_R:
+					case ZEND_FE_FETCH_RW:
+						if (opline->op2_type == IS_CV) {
+							old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var));
+							SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1);
+						}
+						exit_opline = (trace->opline == opline + 1) ?
+							ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
+							opline + 1;
+						break;
+
+				}
+			}
+
+			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
+				old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
+			}
+			exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
+			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
+				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
+			}
+			switch (opline->opcode) {
+				case ZEND_FE_FETCH_R:
+				case ZEND_FE_FETCH_RW:
+					if (opline->op2_type == IS_CV) {
+						SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var), old_info);
+					}
+					break;
+			}
+
+			if (!exit_addr) {
+				return 0;
+			}
+			|	CMP_IP next_opline, TMP1, TMP2
+			|	bne &exit_addr
+		}
+	}
+
+	zend_jit_set_last_valid_opline(trace->opline);
+
+	return 1;
+}
+
+static int zend_jit_handler(dasm_State **Dst, const zend_op *opline, int may_throw)
+{
+	const void *handler;
+
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		handler = zend_get_opcode_handler_func(opline);
+	} else {
+		handler = opline->handler;
+	}
+
+	if (!zend_jit_set_valid_ip(Dst, opline)) {
+		return 0;
+	}
+	if (!GCC_GLOBAL_REGS) {
+		|	mov FCARG1x, FP
+	}
+	|	EXT_CALL handler, REG0
+	if (may_throw) {
+		zend_jit_check_exception(Dst);
+	}
+
+	/* Skip the following OP_DATA */
+	switch (opline->opcode) {
+		case ZEND_ASSIGN_DIM:
+		case ZEND_ASSIGN_OBJ:
+		case ZEND_ASSIGN_STATIC_PROP:
+		case ZEND_ASSIGN_DIM_OP:
+		case ZEND_ASSIGN_OBJ_OP:
+		case ZEND_ASSIGN_STATIC_PROP_OP:
+		case ZEND_ASSIGN_STATIC_PROP_REF:
+		case ZEND_ASSIGN_OBJ_REF:
+			zend_jit_set_last_valid_opline(opline + 2);
+			break;
+		default:
+			zend_jit_set_last_valid_opline(opline + 1);
+			break;
+	}
+
+	return 1;
+}
+
+static int zend_jit_tail_handler(dasm_State **Dst, const zend_op *opline)
+{
+	if (!zend_jit_set_valid_ip(Dst, opline)) {
+		return 0;
+	}
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		if (opline->opcode == ZEND_DO_UCALL ||
+		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
+		    opline->opcode == ZEND_DO_FCALL ||
+		    opline->opcode == ZEND_RETURN) {
+
+			/* Use inlined HYBRID VM handler */
+			const void *handler = opline->handler;
+
+			|	ADD_HYBRID_SPAD
+			|	EXT_JMP handler, REG0
+		} else {
+			const void *handler = zend_get_opcode_handler_func(opline);
+
+			|	EXT_CALL handler, REG0
+			|	ADD_HYBRID_SPAD
+			|	JMP_IP TMP1
+		}
+	} else {
+		const void *handler = opline->handler;
+
+		if (GCC_GLOBAL_REGS) {
+			|	ldp x29, x30, [sp], # SPAD // stack alignment
+		} else {
+			|	mov FCARG1x, FP
+			|	ldp FP, RX, T2                // retore FP and IP
+			|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		}
+		|	EXT_JMP handler, REG0
+	}
+	zend_jit_reset_last_valid_opline();
+	return 1;
+}
+
+static int zend_jit_trace_opline_guard(dasm_State **Dst, const zend_op *opline)
+{
+	uint32_t exit_point = zend_jit_trace_get_exit_point(NULL, 0);
+	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+	if (!exit_addr) {
+		return 0;
+	}
+	|	CMP_IP opline, TMP1, TMP2
+	|	bne &exit_addr
+
+	zend_jit_set_last_valid_opline(opline);
+
+	return 1;
+}
+
+static int zend_jit_jmp(dasm_State **Dst, unsigned int target_label)
+{
+	|	b =>target_label
+	return 1;
+}
+
+static int zend_jit_cond_jmp(dasm_State **Dst, const zend_op *next_opline, unsigned int target_label)
+{
+	|	CMP_IP next_opline, TMP1, TMP2
+	|	bne =>target_label
+
+	zend_jit_set_last_valid_opline(next_opline);
+
+	return 1;
+}
+
+#ifdef CONTEXT_THREADED_JIT
+static int zend_jit_context_threaded_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
+{
+	|	NIY	// TODO
+	return 1;
+}
+#endif
+
+static int zend_jit_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
+{
+#ifdef CONTEXT_THREADED_JIT
+	return zend_jit_context_threaded_call(Dst, opline, next_block);
+#else
+	return zend_jit_tail_handler(Dst, opline);
+#endif
+}
+
+static int zend_jit_spill_store(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info, bool set_type)
+{
+	ZEND_ASSERT(Z_MODE(src) == IS_REG);
+	ZEND_ASSERT(Z_MODE(dst) == IS_MEM_ZVAL);
+
+	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
+		|	SET_ZVAL_LVAL_FROM_REG dst, Rx(Z_REG(src)), TMP1
+		if (set_type) {
+			|	SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2
+		}
+	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+		|	SET_ZVAL_DVAL dst, Z_REG(src), ZREG_TMP1
+		if (set_type) {
+			|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2
+		}
+	} else {
+		ZEND_UNREACHABLE();
+	}
+	return 1;
+}
+
+static int zend_jit_load_reg(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
+{
+	ZEND_ASSERT(Z_MODE(src) == IS_MEM_ZVAL);
+	ZEND_ASSERT(Z_MODE(dst) == IS_REG);
+
+	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
+		|	GET_ZVAL_LVAL Z_REG(dst), src, TMP1
+	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+		|	GET_ZVAL_DVAL Z_REG(dst), src, ZREG_TMP1
+	} else {
+		ZEND_UNREACHABLE();
+	}
+	return 1;
+}
+
+static int zend_jit_store_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg, bool set_type)
+{
+	zend_jit_addr src = ZEND_ADDR_REG(reg);
+	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
+
+	return zend_jit_spill_store(Dst, src, dst, info, set_type);
+}
+
+static int zend_jit_store_var_if_necessary(dasm_State **Dst, int var, zend_jit_addr src, uint32_t info)
+{
+	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
+		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
+		return zend_jit_spill_store(Dst, src, dst, info, 1);
+	}
+	return 1;
+}
+
+static int zend_jit_store_var_if_necessary_ex(dasm_State **Dst, int var, zend_jit_addr src, uint32_t info, zend_jit_addr old, uint32_t old_info)
+{
+	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
+		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
+		bool set_type = 1;
+
+		if ((info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) ==
+		    (old_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF))) {
+			if (Z_MODE(old) != IS_REG || Z_LOAD(old) || Z_STORE(old)) {
+				set_type = 0;
+			}
+		}
+		return zend_jit_spill_store(Dst, src, dst, info, set_type);
+	}
+	return 1;
+}
+
+static int zend_jit_load_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg)
+{
+	zend_jit_addr src = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
+	zend_jit_addr dst = ZEND_ADDR_REG(reg);
+
+	return zend_jit_load_reg(Dst, src, dst, info);
+}
+
+static int zend_jit_update_regs(dasm_State **Dst, uint32_t var, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
+{
+	if (!zend_jit_same_addr(src, dst)) {
+		if (Z_MODE(src) == IS_REG) {
+			if (Z_MODE(dst) == IS_REG) {
+				if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
+					|	mov Rx(Z_REG(dst)), Rx(Z_REG(src))
+				} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+					|	fmov Rd(Z_REG(dst)-ZREG_V0), Rd(Z_REG(src)-ZREG_V0)
+				} else {
+					ZEND_UNREACHABLE();
+				}
+			} else if (Z_MODE(dst) == IS_MEM_ZVAL) {
+				if (!Z_LOAD(src) && !Z_STORE(src)) {
+					if (!zend_jit_spill_store(Dst, src, dst, info,
+							JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
+							JIT_G(current_frame) == NULL ||
+							STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
+							(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
+					)) {
+						return 0;
+					}
+				}
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		} else if (Z_MODE(src) == IS_MEM_ZVAL) {
+			if (Z_MODE(dst) == IS_REG) {
+				if (!zend_jit_load_reg(Dst, src, dst, info)) {
+					return 0;
+				}
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	}
+	return 1;
+}
+
+static int zend_jit_escape_if_undef_r0(dasm_State **Dst, int var, uint32_t flags, const zend_op *opline)
+{
+	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+
+	|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1
+
+	if (flags & ZEND_JIT_EXIT_RESTORE_CALL) {
+		if (!zend_jit_save_call_chain(Dst, -1)) {
+			return 0;
+		}
+	}
+
+	ZEND_ASSERT(opline);
+
+	|	LOAD_IP_ADDR (opline - 1)
+	|	b ->trace_escape
+	|1:
+
+	return 1;
+}
+
+static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
+{
+	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
+
+	if (reg == ZREG_LONG_MIN_MINUS_1) {
+		uint64_t val = 0xc3e0000000000000;
+		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
+		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2
+	} else if (reg == ZREG_LONG_MIN) {
+		uint64_t val = 0x8000000000000000;
+		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
+		|	SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2
+	} else if (reg == ZREG_LONG_MAX) {
+		uint64_t val = 0x7fffffffffffffff;
+		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
+		|	SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2
+	} else if (reg == ZREG_LONG_MAX_PLUS_1) {
+		uint64_t val = 0x43e0000000000000;
+		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
+		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2
+	} else if (reg == ZREG_NULL) {
+		|	SET_ZVAL_TYPE_INFO dst, IS_NULL, TMP1w, TMP2
+	} else if (reg == ZREG_ZVAL_TRY_ADDREF) {
+		|	IF_NOT_ZVAL_REFCOUNTED dst, >1, ZREG_TMP1, ZREG_TMP2
+		|	GET_ZVAL_PTR TMP1, dst, TMP2
+		|	GC_ADDREF TMP1, TMP2w
+		|1:
+	} else if (reg == ZREG_ZVAL_COPY_GPR0) {
+		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+
+		|	ZVAL_COPY_VALUE dst, -1, val_addr, -1, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		|	TRY_ADDREF -1, REG1w, REG2, TMP1w
+	} else {
+		ZEND_UNREACHABLE();
+	}
+	return 1;
+}
+
+static int zend_jit_free_trampoline(dasm_State **Dst)
+{
+	|	// if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
+	|	ldr TMP1w, [REG0, #offsetof(zend_function, common.fn_flags)]
+	|	TST_32_WITH_CONST TMP1w, ZEND_ACC_CALL_VIA_TRAMPOLINE, TMP2w
+	|	beq >1
+	|	mov FCARG1x, REG0
+	|	EXT_CALL zend_jit_free_trampoline_helper, REG0
+	|1:
+	return 1;
+}
+
+static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op1_def_info, zend_jit_addr op1_def_addr, uint32_t res_use_info, uint32_t res_info, zend_jit_addr res_addr, int may_overflow, int may_throw)
+{
+	if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_LONG)) {
+		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2, ZREG_TMP1
+	}
+	if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
+		|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+	}
+	if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, MAY_BE_LONG)) {
+		return 0;
+	}
+	if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
+		|	LONG_ADD_SUB_WITH_IMM adds, op1_def_addr, Z_L(1), TMP1, TMP2
+	} else {
+		|	LONG_ADD_SUB_WITH_IMM subs, op1_def_addr, Z_L(1), TMP1, TMP2
+	}
+
+	if (may_overflow &&
+	    (((op1_def_info & MAY_BE_GUARD) && (op1_def_info & MAY_BE_LONG)) ||
+	     ((opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD) && (res_info & MAY_BE_LONG))))) {
+		int32_t exit_point;
+		const void *exit_addr;
+		zend_jit_trace_stack *stack;
+		uint32_t old_op1_info, old_res_info = 0;
+
+		stack = JIT_G(current_frame)->stack;
+		old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
+		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_DOUBLE, 0);
+		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
+			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MAX_PLUS_1);
+		} else {
+			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MIN_MINUS_1);
+		}
+		if (opline->result_type != IS_UNUSED) {
+			old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+			if (opline->opcode == ZEND_PRE_INC) {
+				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
+				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX_PLUS_1);
+			} else if (opline->opcode == ZEND_PRE_DEC) {
+				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
+				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN_MINUS_1);
+			} else if (opline->opcode == ZEND_POST_INC) {
+				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
+				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX);
+			} else if (opline->opcode == ZEND_POST_DEC) {
+				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
+				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN);
+			}
+		}
+
+		exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+		|	bvs &exit_addr
+
+		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
+		    opline->result_type != IS_UNUSED) {
+			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		}
+
+		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
+		if (opline->result_type != IS_UNUSED) {
+			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
+		}
+	} else if (may_overflow) {
+		|	bvs >1
+		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
+		    opline->result_type != IS_UNUSED) {
+			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		}
+		|.cold_code
+		|1:
+		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
+			uint64_t val = 0x43e0000000000000;
+			if (Z_MODE(op1_def_addr) == IS_REG) {
+				|	LOAD_64BIT_VAL TMP1, val
+				|	fmov Rd(Z_REG(op1_def_addr)-ZREG_V0), TMP1
+			} else {
+				|	SET_ZVAL_LVAL op1_def_addr, val, REG0, TMP1
+			}
+		} else {
+			uint64_t val = 0xc3e0000000000000;
+			if (Z_MODE(op1_def_addr) == IS_REG) {
+				|	LOAD_64BIT_VAL TMP1, val
+				|	fmov Rd(Z_REG(op1_def_addr)-ZREG_V0), TMP1
+			} else {
+				|	SET_ZVAL_LVAL op1_def_addr, val, REG0, TMP1
+			}
+		}
+		if (Z_MODE(op1_def_addr) == IS_MEM_ZVAL) {
+			|	SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE, TMP1w, TMP2
+		}
+		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
+		    opline->result_type != IS_UNUSED) {
+			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_DOUBLE, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		}
+		|	b >3
+		|.code
+	} else {
+		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
+		    opline->result_type != IS_UNUSED) {
+			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		}
+	}
+	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
+		|.cold_code
+		|2:
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
+			|	SET_EX_OPLINE opline, REG0
+			if (op1_info & MAY_BE_UNDEF) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >2, ZREG_TMP1
+				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2
+				|	EXT_CALL zend_jit_undefined_op_helper, REG0
+				op1_info |= MAY_BE_NULL;
+			}
+			|2:
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+
+			|	// ZVAL_DEREF(var_ptr);
+			if (op1_info & MAY_BE_REF) {
+				|	IF_NOT_Z_TYPE, FCARG1x, IS_REFERENCE, >2, TMP1w
+				|	GET_Z_PTR FCARG1x, FCARG1x
+				|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
+				|	cbz TMP1, >1
+				if (RETURN_VALUE_USED(opline)) {
+					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
+				} else {
+					|	mov FCARG2x, xzr
+				}
+				if (opline->opcode == ZEND_PRE_INC) {
+					|	EXT_CALL zend_jit_pre_inc_typed_ref, REG0
+				} else if (opline->opcode == ZEND_PRE_DEC) {
+					|	EXT_CALL zend_jit_pre_dec_typed_ref, REG0
+				} else if (opline->opcode == ZEND_POST_INC) {
+					|	EXT_CALL zend_jit_post_inc_typed_ref, REG0
+				} else if (opline->opcode == ZEND_POST_DEC) {
+					|	EXT_CALL zend_jit_post_dec_typed_ref, REG0
+				} else {
+					ZEND_UNREACHABLE();
+				}
+				zend_jit_check_exception(Dst);
+				|	b >3
+				|1:
+				|	add FCARG1x, FCARG1x, #offsetof(zend_reference, val)
+				|2:
+			}
+
+			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
+				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+
+				|	ZVAL_COPY_VALUE res_addr, res_use_info, val_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
+			}
+			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
+				if (opline->opcode == ZEND_PRE_INC && opline->result_type != IS_UNUSED) {
+					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
+					|	EXT_CALL zend_jit_pre_inc, REG0
+				} else {
+					|	EXT_CALL increment_function, REG0
+				}
+			} else {
+				if (opline->opcode == ZEND_PRE_DEC && opline->result_type != IS_UNUSED) {
+					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
+					|	EXT_CALL zend_jit_pre_dec, REG0
+				} else {
+					|	EXT_CALL decrement_function, REG0
+				}
+			}
+			if (may_throw) {
+				zend_jit_check_exception(Dst);
+			}
+		} else {
+			zend_reg tmp_reg;
+
+			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
+				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_DOUBLE, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			}
+			if (Z_MODE(op1_def_addr) == IS_REG) {
+				tmp_reg = Z_REG(op1_def_addr);
+			} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
+				tmp_reg = Z_REG(op1_addr);
+			} else {
+				tmp_reg = ZREG_FPR0;
+			}
+			|	GET_ZVAL_DVAL tmp_reg, op1_addr, ZREG_TMP1
+			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
+				uint64_t val = 0x3ff0000000000000; // 1.0
+				|	LOAD_64BIT_VAL TMP1, val
+				|	fmov FPTMPd, TMP1
+				|	fadd Rd(tmp_reg-ZREG_V0), Rd(tmp_reg-ZREG_V0), FPTMPd
+			} else {
+				uint64_t val = 0x3ff0000000000000; // 1.0
+				|	LOAD_64BIT_VAL TMP1, val
+				|	fmov FPTMPd, TMP1
+				|	fsub Rd(tmp_reg-ZREG_V0), Rd(tmp_reg-ZREG_V0), FPTMPd
+			}
+			|	SET_ZVAL_DVAL op1_def_addr, tmp_reg, ZREG_TMP1
+			if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
+			    opline->result_type != IS_UNUSED) {
+				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, op1_def_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+				|	TRY_ADDREF op1_def_info, REG0w, REG1, TMP1w
+			}
+		}
+		|	b >3
+		|.code
+	}
+	|3:
+	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_def_addr, op1_def_info, op1_addr, op1_info)) {
+		return 0;
+	}
+	if (opline->result_type != IS_UNUSED) {
+		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int zend_jit_opline_uses_reg(const zend_op  *opline, int8_t reg)
+{
+	if ((opline+1)->opcode == ZEND_OP_DATA
+	 && ((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV))
+	 && JIT_G(current_frame)->stack[EX_VAR_TO_NUM((opline+1)->op1.var)].reg == reg) {
+		return 1;
+	}
+	return
+		((opline->result_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
+			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->result.var)].reg == reg) ||
+		((opline->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
+			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op1.var)].reg == reg) ||
+		((opline->op2_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
+			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op2.var)].reg == reg);
+}
+
+static int zend_jit_math_long_long(dasm_State    **Dst,
+                                   const zend_op  *opline,
+                                   zend_uchar      opcode,
+                                   zend_jit_addr   op1_addr,
+                                   zend_jit_addr   op2_addr,
+                                   zend_jit_addr   res_addr,
+                                   uint32_t        res_info,
+                                   uint32_t        res_use_info,
+                                   int             may_overflow)
+{
+	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+	zend_reg result_reg;
+	zend_reg tmp_reg = ZREG_REG0;
+	bool use_ovf_flag = 1;
+
+	if (Z_MODE(res_addr) == IS_REG && (res_info & MAY_BE_LONG)) {
+		if (may_overflow && (res_info & MAY_BE_GUARD)
+		 && JIT_G(current_frame)
+		 && zend_jit_opline_uses_reg(opline, Z_REG(res_addr))) {
+			result_reg = ZREG_REG0;
+		} else {
+			result_reg = Z_REG(res_addr);
+		}
+	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr) && !may_overflow) {
+		result_reg = Z_REG(op1_addr);
+	} else if (Z_REG(res_addr) != ZREG_REG0) {
+		result_reg = ZREG_REG0;
+	} else {
+		/* ASSIGN_DIM_OP */
+		result_reg = ZREG_FCARG1x;
+		tmp_reg = ZREG_FCARG1x;
+	}
+
+	if (opcode == ZEND_MUL &&
+			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
+			Z_LVAL_P(Z_ZV(op2_addr)) == 2) {
+		if (Z_MODE(op1_addr) == IS_REG) {
+			|	adds Rx(result_reg), Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr))
+		} else {
+			|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+			|	adds Rx(result_reg), Rx(result_reg), Rx(result_reg)
+		}
+	} else if (opcode == ZEND_MUL &&
+			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
+			!may_overflow &&
+			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr)))) {
+		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+		|	mov TMP1, #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
+		|	lsl Rx(result_reg), Rx(result_reg), TMP1
+	} else if (opcode == ZEND_MUL &&
+			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
+			Z_LVAL_P(Z_ZV(op1_addr)) == 2) {
+		if (Z_MODE(op2_addr) == IS_REG) {
+			|	adds Rx(result_reg), Rx(Z_REG(op2_addr)), Rx(Z_REG(op2_addr))
+		} else {
+			|	GET_ZVAL_LVAL result_reg, op2_addr, TMP1
+			|	adds Rx(result_reg), Rx(result_reg), Rx(result_reg)
+		}
+	} else if (opcode == ZEND_MUL &&
+			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
+			!may_overflow &&
+			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op1_addr)))) {
+		|	GET_ZVAL_LVAL result_reg, op2_addr, TMP1
+		|	mov TMP1, #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
+		|	lsl Rx(result_reg), Rx(result_reg), TMP1
+	} else if (opcode == ZEND_DIV &&
+			(Z_MODE(op2_addr) == IS_CONST_ZVAL &&
+			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr))))) {
+		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+		|	asr Rx(result_reg), Rx(result_reg), #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
+#if 0
+	/* x86 specific optimizations through LEA instraction are not supported on ARM */
+	} else if (opcode == ZEND_ADD &&
+			!may_overflow &&
+			Z_MODE(op1_addr) == IS_REG &&
+			Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+		|	NIY	// TODO: test
+	} else if (opcode == ZEND_ADD &&
+			!may_overflow &&
+			Z_MODE(op2_addr) == IS_REG &&
+			Z_MODE(op1_addr) == IS_CONST_ZVAL) {
+		|	NIY	// TODO: test
+	} else if (opcode == ZEND_SUB &&
+			!may_overflow &&
+			Z_MODE(op1_addr) == IS_REG &&
+			Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+		|	NIY	// TODO: test
+#endif
+	} else if (opcode == ZEND_MUL) {
+		|	GET_ZVAL_LVAL ZREG_TMP1, op1_addr, TMP1
+		|	GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2
+		|	mul Rx(result_reg), TMP1, TMP2
+		if(may_overflow) {
+			/* Use 'smulh' to get the upper 64 bits fo the 128-bit result.
+			 * For signed multiplication, the top 65 bits of the result will contain
+			 * either all zeros or all ones if no overflow occurred.
+			 * Note that 'cmp, TMP1, Rx(result_reg), asr, #63' is not supported by DynASM/arm64
+			 * currently, and we put 'asr' and 'cmp' separately.
+			 * Flag: bne -> overflow. beq -> no overflow.
+			 */
+			use_ovf_flag = 0;
+			|	smulh TMP1, TMP1, TMP2
+			|	cmp TMP1, Rx(result_reg), asr #63
+		}
+	} else {
+		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+		if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
+		 && Z_MODE(op2_addr) == IS_CONST_ZVAL
+		 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
+			/* +/- 0 */
+			may_overflow = 0;
+		} else if (same_ops && opcode != ZEND_DIV) {
+			|	LONG_MATH_REG opcode, Rx(result_reg), Rx(result_reg), Rx(result_reg)
+		} else {
+			|	LONG_MATH opcode, result_reg, op2_addr, TMP1
+		}
+	}
+	if (may_overflow) {
+		if (res_info & MAY_BE_GUARD) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+			if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) {
+				if (use_ovf_flag) {
+					|	bvs &exit_addr
+				} else {
+					|	bne &exit_addr
+				}
+				if (Z_MODE(res_addr) == IS_REG && result_reg != Z_REG(res_addr)) {
+					|	mov Rx(Z_REG(res_addr)), Rx(result_reg)
+				}
+			} else if ((res_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+				if (use_ovf_flag) {
+					|	bvc &exit_addr
+				} else {
+					|	beq &exit_addr
+				}
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		} else {
+			if (res_info & MAY_BE_LONG) {
+				if (use_ovf_flag) {
+					|	bvs >1
+				} else {
+					|	bne >1
+				}
+			} else {
+				if (use_ovf_flag) {
+					|	bvc >1
+				} else {
+					|	beq >1
+				}
+			}
+		}
+	}
+
+	if (Z_MODE(res_addr) == IS_MEM_ZVAL && (res_info & MAY_BE_LONG)) {
+		|	SET_ZVAL_LVAL_FROM_REG res_addr, Rx(result_reg), TMP1
+		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
+			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+			}
+		}
+	}
+
+	if (may_overflow && (!(res_info & MAY_BE_GUARD) || (res_info & MAY_BE_ANY) == MAY_BE_DOUBLE)) {
+		zend_reg tmp_reg1 = ZREG_FPR0;
+		zend_reg tmp_reg2 = ZREG_FPR1;
+
+		if (res_info & MAY_BE_LONG) {
+			|.cold_code
+			|1:
+		}
+
+		do {
+			if ((Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 1) ||
+			    (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 1)) {
+				if (opcode == ZEND_ADD) {
+					uint64_t val = 0x43e0000000000000;
+					if (Z_MODE(res_addr) == IS_REG) {
+						|	LOAD_64BIT_VAL TMP1, val
+						|	fmov Rd(Z_REG(res_addr)-ZREG_V0), TMP1
+					} else {
+						|	SET_ZVAL_LVAL res_addr, val, REG0, TMP1
+					}
+					break;
+				} else if (opcode == ZEND_SUB) {
+					uint64_t val = 0xc3e0000000000000;
+					if (Z_MODE(res_addr) == IS_REG) {
+						|	LOAD_64BIT_VAL TMP1, val
+						|	fmov Rd(Z_REG(res_addr)-ZREG_V0), TMP1
+					} else {
+						|	SET_ZVAL_LVAL res_addr, val, REG0, TMP1
+					}
+					break;
+				}
+			}
+
+			|	DOUBLE_GET_ZVAL_LVAL tmp_reg1, op1_addr, tmp_reg, ZREG_TMP1
+			|	DOUBLE_GET_ZVAL_LVAL tmp_reg2, op2_addr, tmp_reg, ZREG_TMP1
+			|	DOUBLE_MATH_REG opcode, tmp_reg1, tmp_reg1, tmp_reg2
+			|	SET_ZVAL_DVAL res_addr, tmp_reg1, ZREG_TMP1
+		} while (0);
+
+		if (Z_MODE(res_addr) == IS_MEM_ZVAL
+		 && (res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
+		}
+		if (res_info & MAY_BE_LONG) {
+			|	b >2
+			|.code
+		}
+		|2:
+	}
+
+	return 1;
+}
+
+static int zend_jit_math_long_double(dasm_State    **Dst,
+                                     zend_uchar      opcode,
+                                     zend_jit_addr   op1_addr,
+                                     zend_jit_addr   op2_addr,
+                                     zend_jit_addr   res_addr,
+                                     uint32_t        res_use_info)
+{
+	zend_reg result_reg =
+		(Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : ZREG_FPR0;
+	zend_reg op2_reg;
+
+	|	DOUBLE_GET_ZVAL_LVAL result_reg, op1_addr, ZREG_TMP1, ZREG_TMP2
+
+	if (Z_MODE(op2_addr) == IS_REG) {
+		op2_reg = Z_REG(op2_addr);
+	} else {
+		op2_reg = ZREG_FPTMP;
+		|	GET_ZVAL_DVAL op2_reg, op2_addr, ZREG_TMP1
+	}
+
+	|	DOUBLE_MATH_REG opcode, result_reg, result_reg, op2_reg
+
+	|	SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1
+
+	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+		if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_math_double_long(dasm_State    **Dst,
+                                     zend_uchar      opcode,
+                                     zend_jit_addr   op1_addr,
+                                     zend_jit_addr   op2_addr,
+                                     zend_jit_addr   res_addr,
+                                     uint32_t        res_use_info)
+{
+	zend_reg result_reg, op1_reg, op2_reg;
+
+	if (zend_is_commutative(opcode)
+	 && (Z_MODE(res_addr) != IS_REG || Z_MODE(op1_addr) != IS_REG || Z_REG(res_addr) != Z_REG(op1_addr))) {
+		if (Z_MODE(res_addr) == IS_REG) {
+			result_reg = Z_REG(res_addr);
+		} else {
+			result_reg = ZREG_FPR0;
+		}
+		|	DOUBLE_GET_ZVAL_LVAL result_reg, op2_addr, ZREG_TMP1, ZREG_TMP2
+		if (Z_MODE(op1_addr) == IS_REG) {
+			op1_reg = Z_REG(op1_addr);
+		} else {
+			op1_reg = ZREG_FPTMP;
+			|	GET_ZVAL_DVAL op1_reg, op1_addr, ZREG_TMP1
+		}
+		|	DOUBLE_MATH_REG opcode, result_reg, result_reg, op1_reg
+	} else {
+		if (Z_MODE(res_addr) == IS_REG) {
+			result_reg = Z_REG(res_addr);
+		} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
+			result_reg = Z_REG(op1_addr);
+		} else {
+			result_reg = ZREG_FPR0;
+		}
+
+		if (Z_MODE(op1_addr) == IS_REG) {
+			op1_reg = Z_REG(op1_addr);
+		} else {
+			|	GET_ZVAL_DVAL result_reg, op1_addr, ZREG_TMP1
+			op1_reg = result_reg;
+		}
+		if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
+		 && Z_MODE(op2_addr) == IS_CONST_ZVAL
+		 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
+			/* +/- 0 */
+		} else {
+			op2_reg = ZREG_FPTMP;
+			|	DOUBLE_GET_ZVAL_LVAL op2_reg, op2_addr, ZREG_TMP1, ZREG_TMP2
+			|	DOUBLE_MATH_REG opcode, result_reg, op1_reg, op2_reg
+		}
+	}
+
+	|	SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1
+
+	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
+			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
+			}
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_math_double_double(dasm_State    **Dst,
+                                       zend_uchar      opcode,
+                                       zend_jit_addr   op1_addr,
+                                       zend_jit_addr   op2_addr,
+                                       zend_jit_addr   res_addr,
+                                       uint32_t        res_use_info)
+{
+	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+	zend_reg result_reg, op1_reg, op2_reg;
+	zend_jit_addr val_addr;
+
+	if (Z_MODE(res_addr) == IS_REG) {
+		result_reg = Z_REG(res_addr);
+	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
+		result_reg = Z_REG(op1_addr);
+	} else if (zend_is_commutative(opcode) && Z_MODE(op2_addr) == IS_REG && Z_LAST_USE(op2_addr)) {
+		result_reg = Z_REG(op2_addr);
+	} else {
+		result_reg = ZREG_FPR0;
+	}
+
+	if (Z_MODE(op1_addr) == IS_REG) {
+		op1_reg = Z_REG(op1_addr);
+		val_addr = op2_addr;
+	} else if (Z_MODE(op2_addr) == IS_REG && zend_is_commutative(opcode)) {
+		op1_reg = Z_REG(op2_addr);
+		val_addr = op1_addr;
+	} else {
+		|	GET_ZVAL_DVAL result_reg, op1_addr, ZREG_TMP1
+		op1_reg = result_reg;
+		val_addr = op2_addr;
+	}
+
+	if ((opcode == ZEND_MUL) &&
+		Z_MODE(val_addr) == IS_CONST_ZVAL && Z_DVAL_P(Z_ZV(val_addr)) == 2.0) {
+		|	DOUBLE_MATH_REG ZEND_ADD, result_reg, op1_reg, op1_reg
+	} else {
+		if (same_ops) {
+			op2_reg = op1_reg;
+		} else if (Z_MODE(val_addr) == IS_REG) {
+			op2_reg = Z_REG(val_addr);
+		} else {
+			op2_reg = ZREG_FPTMP;
+			|	GET_ZVAL_DVAL op2_reg, val_addr, ZREG_TMP1
+		}
+		|	DOUBLE_MATH_REG opcode, result_reg, op1_reg, op2_reg
+	}
+
+	|	SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1
+
+	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
+			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
+			}
+		}
+	}
+	return 1;
+}
+
+static int zend_jit_math_helper(dasm_State    **Dst,
+                                const zend_op  *opline,
+                                zend_uchar      opcode,
+                                zend_uchar      op1_type,
+                                znode_op        op1,
+                                zend_jit_addr   op1_addr,
+                                uint32_t        op1_info,
+                                zend_uchar      op2_type,
+                                znode_op        op2,
+                                zend_jit_addr   op2_addr,
+                                uint32_t        op2_info,
+                                uint32_t        res_var,
+                                zend_jit_addr   res_addr,
+                                uint32_t        res_info,
+                                uint32_t        res_use_info,
+                                int             may_overflow,
+                                int             may_throw)
+/* Labels: 1,2,3,4,5,6 */
+{
+	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+
+	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
+		if (op1_info & (MAY_BE_ANY-MAY_BE_LONG)) {
+			if (op1_info & MAY_BE_DOUBLE) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1
+			} else {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
+			}
+		}
+		if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_LONG))) {
+			if (op2_info & MAY_BE_DOUBLE) {
+				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >1, ZREG_TMP1
+				|.cold_code
+				|1:
+				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1
+				}
+				if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
+					return 0;
+				}
+				|	b >5
+				|.code
+			} else {
+				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
+			}
+		}
+		if (!zend_jit_math_long_long(Dst, opline, opcode, op1_addr, op2_addr, res_addr, res_info, res_use_info, may_overflow)) {
+			return 0;
+		}
+		if (op1_info & MAY_BE_DOUBLE) {
+			|.cold_code
+			|3:
+			if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1
+			}
+			if (op2_info & MAY_BE_DOUBLE) {
+				if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
+					if (!same_ops) {
+						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >1, ZREG_TMP1
+					} else {
+						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >6, ZREG_TMP1
+					}
+				}
+				if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
+					return 0;
+				}
+				|	b >5
+			}
+			if (!same_ops) {
+				|1:
+				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
+				}
+				if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
+					return 0;
+				}
+				|	b >5
+			}
+			|.code
+		}
+	} else if ((op1_info & MAY_BE_DOUBLE) &&
+	           !(op1_info & MAY_BE_LONG) &&
+	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+		if (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1
+		}
+		if (op2_info & MAY_BE_DOUBLE) {
+			if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
+				if (!same_ops && (op2_info & MAY_BE_LONG)) {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >1, ZREG_TMP1
+				} else {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1
+				}
+			}
+			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
+				return 0;
+			}
+		}
+		if (!same_ops && (op2_info & MAY_BE_LONG)) {
+			if (op2_info & MAY_BE_DOUBLE) {
+				|.cold_code
+			}
+		    |1:
+			if (op2_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
+				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
+			}
+			if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
+				return 0;
+			}
+			if (op2_info & MAY_BE_DOUBLE) {
+				|	b >5
+				|.code
+			}
+		}
+	} else if ((op2_info & MAY_BE_DOUBLE) &&
+	           !(op2_info & MAY_BE_LONG) &&
+	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+		if (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
+			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1
+		}
+		if (op1_info & MAY_BE_DOUBLE) {
+			if (!same_ops && (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
+				if (!same_ops && (op1_info & MAY_BE_LONG)) {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >1, ZREG_TMP1
+				} else {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1
+				}
+			}
+			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
+				return 0;
+			}
+		}
+		if (!same_ops && (op1_info & MAY_BE_LONG)) {
+			if (op1_info & MAY_BE_DOUBLE) {
+				|.cold_code
+			}
+			|1:
+			if (op1_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
+			}
+			if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
+				return 0;
+			}
+			if (op1_info & MAY_BE_DOUBLE) {
+				|	b >5
+				|.code
+			}
+		}
+	}
+
+	|5:
+
+	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
+		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
+		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+			|.cold_code
+		}
+		|6:
+		if (Z_MODE(res_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
+			|	LOAD_ZVAL_ADDR FCARG1x, real_addr
+		} else if (Z_REG(res_addr) != ZREG_FCARG1x || Z_OFFSET(res_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+		}
+		if (Z_MODE(op1_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
+			if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
+				return 0;
+			}
+			op1_addr = real_addr;
+		}
+		|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
+		if (Z_MODE(op2_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
+			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
+				return 0;
+			}
+			op2_addr = real_addr;
+		}
+		|	LOAD_ZVAL_ADDR CARG3, op2_addr
+		|	SET_EX_OPLINE opline, REG0
+		if (opcode == ZEND_ADD) {
+			|	EXT_CALL add_function, REG0
+		} else if (opcode == ZEND_SUB) {
+			|	EXT_CALL sub_function, REG0
+		} else if (opcode == ZEND_MUL) {
+			|	EXT_CALL mul_function, REG0
+		} else if (opcode == ZEND_DIV) {
+			|	EXT_CALL div_function, REG0
+		} else {
+			ZEND_UNREACHABLE();
+		}
+		|	FREE_OP op1_type, op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|	FREE_OP op2_type, op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		if (may_throw) {
+			zend_jit_check_exception(Dst);
+		}
+		if (Z_MODE(res_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
+			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
+				return 0;
+			}
+		}
+		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+			|	b <5
+			|.code
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_math(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, uint32_t res_use_info, uint32_t res_info, zend_jit_addr res_addr, int may_overflow, int may_throw)
+{
+	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
+	ZEND_ASSERT((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+	    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)));
+
+	if (!zend_jit_math_helper(Dst, opline, opline->opcode, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, opline->result.var, res_addr, res_info, res_use_info, may_overflow, may_throw)) {
+		return 0;
+	}
+	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
+		return 0;
+	}
+	return 1;
+}
+
+static int zend_jit_add_arrays(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint32_t op2_info, zend_jit_addr res_addr)
+{
+	zend_jit_addr op1_addr = OP1_ADDR();
+	zend_jit_addr op2_addr = OP2_ADDR();
+
+	|	GET_ZVAL_LVAL ZREG_FCARG1x, op1_addr, TMP1
+	|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+	|	EXT_CALL zend_jit_add_arrays_helper, REG0
+	|	SET_ZVAL_PTR res_addr, RETVALx, TMP1
+	|	SET_ZVAL_TYPE_INFO res_addr, IS_ARRAY_EX, TMP1w, TMP2
+	|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	return 1;
+}
+
+static int zend_jit_long_math_helper(dasm_State    **Dst,
+                                     const zend_op  *opline,
+                                     zend_uchar      opcode,
+                                     zend_uchar      op1_type,
+                                     znode_op        op1,
+                                     zend_jit_addr   op1_addr,
+                                     uint32_t        op1_info,
+                                     zend_ssa_range *op1_range,
+                                     zend_uchar      op2_type,
+                                     znode_op        op2,
+                                     zend_jit_addr   op2_addr,
+                                     uint32_t        op2_info,
+                                     zend_ssa_range *op2_range,
+                                     uint32_t        res_var,
+                                     zend_jit_addr   res_addr,
+                                     uint32_t        res_info,
+                                     uint32_t        res_use_info,
+                                     int             may_throw)
+/* Labels: 6 */
+{
+	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+	zend_reg result_reg;
+	zval tmp;
+
+	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
+		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
+	}
+	if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
+		|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
+	}
+
+	if (opcode == ZEND_MOD && Z_MODE(op2_addr) == IS_CONST_ZVAL &&
+	    op1_range &&
+	    op1_range->min >= 0) {
+		zend_long l = Z_LVAL_P(Z_ZV(op2_addr));
+
+		if (zend_long_is_power_of_two(l)) {
+			/* Optimisation for mod of power of 2 */
+			opcode = ZEND_BW_AND;
+			ZVAL_LONG(&tmp, l - 1);
+			op2_addr = ZEND_ADDR_CONST_ZVAL(&tmp);
+		}
+	}
+
+	if (opcode == ZEND_MOD) {
+		result_reg = ZREG_REG0;
+	} else if (Z_MODE(res_addr) == IS_REG) {
+		if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR)
+		 && opline->op2_type != IS_CONST) {
+			result_reg = ZREG_REG0;
+		} else {
+			result_reg = Z_REG(res_addr);
+		}
+	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
+		result_reg = Z_REG(op1_addr);
+	} else if (Z_REG(res_addr) != ZREG_REG0) {
+		result_reg = ZREG_REG0;
+	} else {
+		/* ASSIGN_DIM_OP */
+		result_reg = ZREG_FCARG1x;
+	}
+
+	if (opcode == ZEND_SL) {
+		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
+
+			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
+				if (EXPECTED(op2_lval > 0)) {
+					|	mov Rx(result_reg), xzr
+				} else {
+					|	SET_EX_OPLINE opline, REG0
+					|	b ->negative_shift
+				}
+			} else if (Z_MODE(op1_addr) == IS_REG && op2_lval == 1) {
+				|	add Rx(result_reg), Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr))
+			} else {
+				|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+				|	mov TMP1w, #op2_lval
+				|	lsl Rx(result_reg), Rx(result_reg), TMP1
+			}
+		} else {
+			if (Z_MODE(op2_addr) != IS_REG || Z_REG(op2_addr) != ZREG_REG1) {
+				|	GET_ZVAL_LVAL ZREG_REG1, op2_addr, TMP1
+			}
+			if (!op2_range ||
+			     op2_range->min < 0 ||
+			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
+
+				|	cmp REG1, #(SIZEOF_ZEND_LONG*8)
+				|	bhs >1
+				|.cold_code
+				|1:
+				|	mov Rx(result_reg), xzr
+				|	cmp REG1, xzr
+				|	bgt >1
+				|	SET_EX_OPLINE opline, REG0
+				|	b ->negative_shift
+				|.code
+			}
+			|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+			|	lsl Rx(result_reg), Rx(result_reg), REG1
+			|1:
+		}
+	} else if (opcode == ZEND_SR) {
+		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
+
+			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
+				if (EXPECTED(op2_lval > 0)) {
+					|	asr Rx(result_reg), Rx(result_reg), #((SIZEOF_ZEND_LONG * 8) - 1)
+				} else {
+					|	SET_EX_OPLINE opline, REG0
+					|	b ->negative_shift
+				}
+			} else {
+				|	mov TMP1w, #op2_lval
+				|	asr Rx(result_reg), Rx(result_reg), TMP1
+			}
+		} else {
+			if (Z_MODE(op2_addr) != IS_REG || Z_REG(op2_addr) != ZREG_REG1) {
+				|	GET_ZVAL_LVAL ZREG_REG1, op2_addr, TMP1
+			}
+			if (!op2_range ||
+			     op2_range->min < 0 ||
+			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
+				|	cmp REG1, #(SIZEOF_ZEND_LONG*8)
+				|	bhs >1
+				|.cold_code
+				|1:
+				|	cmp REG1, xzr
+				|	mov REG1w, #((SIZEOF_ZEND_LONG * 8) - 1)
+				|	bgt >1
+				|	SET_EX_OPLINE opline, REG0
+				|	b ->negative_shift
+				|.code
+			}
+			|1:
+			|	asr Rx(result_reg), Rx(result_reg), REG1
+		}
+	} else if (opcode == ZEND_MOD) {
+		// REG0 -> result_reg
+		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
+
+			if (op2_lval == 0) {
+				|	SET_EX_OPLINE opline, REG0
+				|	b ->mod_by_zero
+			} else if (op2_lval == -1) {
+				|	mov REG0, xzr
+			} else {
+				|	GET_ZVAL_LVAL ZREG_REG1, op1_addr, TMP1
+				|	GET_ZVAL_LVAL ZREG_REG2, op2_addr, TMP1
+				|	sdiv REG0, REG1, REG2
+				|	msub REG0, REG0, REG2, REG1
+			}
+		} else {
+			if (!op2_range || (op2_range->min <= 0 && op2_range->max >= 0)) {
+				if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
+					|	SAFE_MEM_ACC_WITH_UOFFSET ldr, TMP1, Rx(Z_REG(op2_addr)), Z_OFFSET(op2_addr), TMP2
+					|	cbz TMP1, >1
+				} else if (Z_MODE(op2_addr) == IS_REG) {
+					|	cbz Rx(Z_REG(op2_addr)), >1
+				}
+				|.cold_code
+				|1:
+				|	SET_EX_OPLINE opline, REG0
+				|	b ->mod_by_zero
+				|.code
+			}
+
+			/* Prevent overflow error/crash if op1 == LONG_MIN and op2 == -1 */
+			if (!op2_range || (op2_range->min <= -1 && op2_range->max >= -1)) {
+				if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
+					|	SAFE_MEM_ACC_WITH_UOFFSET ldr, TMP1, Rx(Z_REG(op2_addr)), Z_OFFSET(op2_addr), TMP2
+					|	cmn TMP1, #1
+				} else if (Z_MODE(op2_addr) == IS_REG) {
+					|	cmn Rx(Z_REG(op2_addr)), #1
+				}
+				|	beq >1
+				|.cold_code
+				|1:
+				|	SET_ZVAL_LVAL_FROM_REG res_addr, xzr, TMP1
+				if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+					if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
+						if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+						}
+					}
+				}
+				|	b >5
+				|.code
+			}
+
+			|	GET_ZVAL_LVAL ZREG_REG1, op1_addr, TMP1
+			if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
+				|	ldr TMP1, [Rx(Z_REG(op2_addr)), #Z_OFFSET(op2_addr)]
+				|	sdiv REG0, REG1, TMP1
+				|	msub REG0, REG0, TMP1, REG1
+			} else if (Z_MODE(op2_addr) == IS_REG) {
+				|	sdiv REG0, REG1, Rx(Z_REG(op2_addr))
+				|	msub REG0, REG0, Rx(Z_REG(op2_addr)), REG1
+			}
+		}
+	} else if (same_ops) {
+		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+		|	LONG_MATH_REG opcode, Rx(result_reg), Rx(result_reg), Rx(result_reg)
+	} else {
+		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
+		|	LONG_MATH opcode, result_reg, op2_addr, TMP1
+	}
+
+	if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != result_reg) {
+		|	SET_ZVAL_LVAL_FROM_REG res_addr, Rx(result_reg), TMP1
+	}
+	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
+			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+			}
+		}
+	}
+
+	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) ||
+		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
+		if ((op1_info & MAY_BE_LONG) &&
+		    (op2_info & MAY_BE_LONG)) {
+			|.cold_code
+		}
+		|6:
+		if (Z_MODE(res_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
+			|	LOAD_ZVAL_ADDR FCARG1x, real_addr
+		} else if (Z_REG(res_addr) != ZREG_FCARG1x || Z_OFFSET(res_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+		}
+		if (Z_MODE(op1_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
+			if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
+				return 0;
+			}
+			op1_addr = real_addr;
+		}
+		|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
+		if (Z_MODE(op2_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
+			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
+				return 0;
+			}
+			op2_addr = real_addr;
+		}
+		|	LOAD_ZVAL_ADDR CARG3, op2_addr
+		|	SET_EX_OPLINE opline, REG0
+		if (opcode == ZEND_BW_OR) {
+			|	EXT_CALL bitwise_or_function, REG0
+		} else if (opcode == ZEND_BW_AND) {
+			|	EXT_CALL bitwise_and_function, REG0
+		} else if (opcode == ZEND_BW_XOR) {
+			|	EXT_CALL bitwise_xor_function, REG0
+		} else if (opcode == ZEND_SL) {
+			|	EXT_CALL shift_left_function, REG0
+		} else if (opcode == ZEND_SR) {
+			|	EXT_CALL shift_right_function, REG0
+		} else if (opcode == ZEND_MOD) {
+			|	EXT_CALL mod_function, REG0
+		} else {
+			ZEND_UNREACHABLE();
+		}
+		|	FREE_OP op1_type, op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|	FREE_OP op2_type, op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		if (may_throw) {
+			zend_jit_check_exception(Dst);
+		}
+		if (Z_MODE(res_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
+			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
+				return 0;
+			}
+		}
+		if ((op1_info & MAY_BE_LONG) &&
+		    (op2_info & MAY_BE_LONG)) {
+			|	b >5
+			|.code
+		}
+	}
+	|5:
+
+	return 1;
+}
+
+static int zend_jit_long_math(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_ssa_range *op1_range, zend_jit_addr op1_addr, uint32_t op2_info, zend_ssa_range *op2_range, zend_jit_addr op2_addr, uint32_t res_use_info, uint32_t res_info, zend_jit_addr res_addr, int may_throw)
+{
+	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
+	ZEND_ASSERT((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG));
+
+	if (!zend_jit_long_math_helper(Dst, opline, opline->opcode,
+			opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
+			opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
+			opline->result.var, res_addr, res_info, res_use_info, may_throw)) {
+		return 0;
+	}
+	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
+		return 0;
+	}
+	return 1;
+}
+
+static int zend_jit_concat_helper(dasm_State    **Dst,
+                                  const zend_op  *opline,
+                                  zend_uchar      op1_type,
+                                  znode_op        op1,
+                                  zend_jit_addr   op1_addr,
+                                  uint32_t        op1_info,
+                                  zend_uchar      op2_type,
+                                  znode_op        op2,
+                                  zend_jit_addr   op2_addr,
+                                  uint32_t        op2_info,
+                                  zend_jit_addr   res_addr,
+                                  int             may_throw)
+{
+	if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
+		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1
+		}
+		if (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
+			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >6, ZREG_TMP1
+		}
+		if (Z_MODE(op1_addr) == IS_MEM_ZVAL && Z_REG(op1_addr) == Z_REG(res_addr) && Z_OFFSET(op1_addr) == Z_OFFSET(res_addr)) {
+			if (Z_REG(res_addr) != ZREG_FCARG1x || Z_OFFSET(res_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+			}
+			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+			|	EXT_CALL zend_jit_fast_assign_concat_helper, REG0
+		} else {
+			if (Z_REG(res_addr) != ZREG_FCARG1x || Z_OFFSET(res_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+			}
+			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
+			|	LOAD_ZVAL_ADDR CARG3, op2_addr
+			|	EXT_CALL zend_jit_fast_concat_helper, REG0
+		}
+		/* concatination with empty string may increase refcount */
+		op1_info |= MAY_BE_RCN;
+		op2_info |= MAY_BE_RCN;
+		|	FREE_OP op1_type, op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|	FREE_OP op2_type, op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|5:
+	}
+	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) ||
+	    (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING))) {
+		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
+			|.cold_code
+			|6:
+		}
+		if (Z_REG(res_addr) != ZREG_FCARG1x || Z_OFFSET(res_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+		}
+		|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
+		|	LOAD_ZVAL_ADDR CARG3, op2_addr
+		|	SET_EX_OPLINE opline, REG0
+		|	EXT_CALL concat_function, REG0
+		/* concatination with empty string may increase refcount */
+		op1_info |= MAY_BE_RCN;
+		op2_info |= MAY_BE_RCN;
+		|	FREE_OP op1_type, op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|	FREE_OP op2_type, op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		if (may_throw) {
+			zend_jit_check_exception(Dst);
+		}
+		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
+			|	b <5
+			|.code
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_concat(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint32_t op2_info, zend_jit_addr res_addr, int may_throw)
+{
+	zend_jit_addr op1_addr, op2_addr;
+
+	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
+	ZEND_ASSERT((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING));
+
+	op1_addr = OP1_ADDR();
+	op2_addr = OP2_ADDR();
+
+	return zend_jit_concat_helper(Dst, opline, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, res_addr, may_throw);
+}
+
+static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_op *opline, uint32_t type, uint32_t op1_info, uint32_t op2_info, const void *found_exit_addr, const void *not_found_exit_addr, const void *exit_addr)
+/* Labels: 1,2,3,4,5 */
+{
+	zend_jit_addr op2_addr = OP2_ADDR();
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
+	 && type == BP_VAR_R
+	 && !exit_addr) {
+		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+		if (!exit_addr) {
+			return 0;
+		}
+	}
+
+	if (op2_info & MAY_BE_LONG) {
+		bool op2_loaded = 0;
+		bool packed_loaded = 0;
+
+		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) {
+			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_LONG))
+			|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3, ZREG_TMP1
+		}
+		if (op1_info & MAY_BE_PACKED_GUARD) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+			if (!exit_addr) {
+				return 0;
+			}
+			if (op1_info & MAY_BE_ARRAY_PACKED) {
+				|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
+				|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
+				|	beq &exit_addr
+			} else {
+				|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
+				|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
+				|	bne &exit_addr
+			}
+		}
+		if (type == BP_VAR_W) {
+			|	// hval = Z_LVAL_P(dim);
+			|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+			op2_loaded = 1;
+		}
+		if (op1_info & MAY_BE_ARRAY_PACKED) {
+			zend_long val = -1;
+
+			if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+				val = Z_LVAL_P(Z_ZV(op2_addr));
+				if (val >= 0 && val < HT_MAX_SIZE) {
+					packed_loaded = 1;
+				}
+			} else {
+				if (!op2_loaded) {
+					|	// hval = Z_LVAL_P(dim);
+					|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+					op2_loaded = 1;
+				}
+				packed_loaded = 1;
+			}
+			if (packed_loaded) {
+				|	// ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
+				if (op1_info & MAY_BE_ARRAY_HASH) {
+					|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
+					|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
+					|	beq >4 // HASH_FIND
+				}
+				|	// if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed))
+
+				|	ldr REG0w, [FCARG1x, #offsetof(zend_array, nNumUsed)]
+				if (val == 0) {
+					|	cmp REG0, xzr
+				} else if (val > 0 && !op2_loaded) {
+					|	CMP_64_WITH_CONST REG0, val, TMP1
+				} else {
+					|	cmp REG0, FCARG2x
+				}
+
+				if (type == BP_JIT_IS) {
+					if (not_found_exit_addr) {
+						|	bls &not_found_exit_addr
+					} else {
+						|	bls >9 // NOT_FOUND
+					}
+				} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
+					|	bls &exit_addr
+				} else if (type == BP_VAR_IS && not_found_exit_addr) {
+					|	bls &not_found_exit_addr
+				} else if (type == BP_VAR_IS && found_exit_addr) {
+					|	bls >7 // NOT_FOUND
+				} else {
+					|	bls >2 // NOT_FOUND
+				}
+				|	// _ret = &_ht->arData[_h].val;
+				if (val >= 0) {
+					|	ldr REG0, [FCARG1x, #offsetof(zend_array, arData)]
+					if (val != 0) {
+						|	ADD_SUB_64_WITH_CONST add, REG0, REG0, (val * sizeof(Bucket)), TMP1
+					}
+				} else {
+					|	ldr TMP1, [FCARG1x, #offsetof(zend_array, arData)]
+					|	add REG0, TMP1, FCARG2x, lsl #5
+				}
+			}
+		}
+		switch (type) {
+			case BP_JIT_IS:
+				if (op1_info & MAY_BE_ARRAY_HASH) {
+					if (packed_loaded) {
+						|	b >5
+					}
+					|4:
+					if (!op2_loaded) {
+						|	// hval = Z_LVAL_P(dim);
+						|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+					}
+					|	EXT_CALL _zend_hash_index_find, REG0
+					|	mov REG0, RETVALx
+					if (not_found_exit_addr) {
+						|	cbz REG0, &not_found_exit_addr
+					} else {
+						|	cbz REG0, >9 // NOT_FOUND
+					}
+					if (op2_info & MAY_BE_STRING) {
+						|	b >5
+					}
+				} else if (packed_loaded) {
+					if (op2_info & MAY_BE_STRING) {
+						|	b >5
+					}
+				} else if (not_found_exit_addr) {
+					|	b &not_found_exit_addr
+				} else {
+					|	b >9 // NOT_FOUND
+				}
+				break;
+			case BP_VAR_R:
+			case BP_VAR_IS:
+			case BP_VAR_UNSET:
+				if (packed_loaded) {
+					if (op1_info & MAY_BE_ARRAY_HASH) {
+						|	IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w
+					} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
+						/* perform IS_UNDEF check only after result type guard (during deoptimization) */
+						if (!found_exit_addr || (op1_info & MAY_BE_ARRAY_HASH)) {
+							|	IF_Z_TYPE REG0, IS_UNDEF, &exit_addr, TMP1w
+						}
+					} else if (type == BP_VAR_IS && not_found_exit_addr) {
+						|	IF_Z_TYPE REG0, IS_UNDEF, &not_found_exit_addr, TMP1w
+					} else if (type == BP_VAR_IS && found_exit_addr) {
+						|	IF_Z_TYPE REG0, IS_UNDEF, >7, TMP1w // NOT_FOUND
+					} else {
+						|	IF_Z_TYPE REG0, IS_UNDEF, >2, TMP1w // NOT_FOUND
+					}
+				}
+				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (packed_loaded && (op1_info & MAY_BE_ARRAY_HASH))) {
+					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
+						|	b &exit_addr
+					} else if (type == BP_VAR_IS && not_found_exit_addr) {
+						|	b &not_found_exit_addr
+					} else if (type == BP_VAR_IS && found_exit_addr) {
+						|	b >7 // NOT_FOUND
+					} else {
+						|	b >2 // NOT_FOUND
+					}
+				}
+				if (op1_info & MAY_BE_ARRAY_HASH) {
+					|4:
+					if (!op2_loaded) {
+						|	// hval = Z_LVAL_P(dim);
+						|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+					}
+					|	EXT_CALL _zend_hash_index_find, REG0
+					|	mov REG0, RETVALx
+					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
+						|	cbz REG0, &exit_addr
+					} else if (type == BP_VAR_IS && not_found_exit_addr) {
+						|	cbz REG0, &not_found_exit_addr
+					} else if (type == BP_VAR_IS && found_exit_addr) {
+						|	cbz REG0, >7 // NOT_FOUND
+					} else {
+						|	cbz REG0, >2 // NOT_FOUND
+					}
+				}
+				|.cold_code
+				|2:
+				switch (type) {
+					case BP_VAR_R:
+						if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
+							|	// zend_error(E_WARNING,"Undefined array key " ZEND_LONG_FMT, hval);
+							|	// retval = &EG(uninitialized_zval);
+							|	UNDEFINED_OFFSET opline
+							|	b >9
+						}
+						break;
+					case BP_VAR_IS:
+					case BP_VAR_UNSET:
+						if (!not_found_exit_addr && !found_exit_addr) {
+							|	// retval = &EG(uninitialized_zval);
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+							|	b >9
+						}
+						break;
+					default:
+						ZEND_UNREACHABLE();
+				}
+				|.code
+				break;
+			case BP_VAR_RW:
+				if (packed_loaded) {
+					|	IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w
+				}
+				|2:
+				|4:
+				if (!op2_loaded) {
+					|	// hval = Z_LVAL_P(dim);
+					|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+				}
+				|	SET_EX_OPLINE opline, REG0
+				|	EXT_CALL zend_jit_hash_index_lookup_rw, REG0
+				|	mov REG0, RETVALx
+				|	cbz REG0, >9
+				break;
+			case BP_VAR_W:
+				if (packed_loaded) {
+					|	IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w
+				}
+				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || packed_loaded) {
+					|2:
+					|	//retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
+					if (!op2_loaded) {
+						|	// hval = Z_LVAL_P(dim);
+						|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+					}
+					|	LOAD_ADDR_ZTS CARG3, executor_globals, uninitialized_zval
+					|	EXT_CALL zend_hash_index_add_new, REG0
+					|	mov REG0, RETVALx
+					if (op1_info & MAY_BE_ARRAY_HASH) {
+						|	b >8
+					}
+				}
+				if (op1_info & MAY_BE_ARRAY_HASH) {
+					|4:
+					if (!op2_loaded) {
+						|	// hval = Z_LVAL_P(dim);
+						|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+					}
+					|	EXT_CALL zend_hash_index_lookup, REG0
+					|	mov REG0, RETVALx
+				}
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+
+		if (type != BP_JIT_IS && (op2_info & MAY_BE_STRING)) {
+			|	b >8
+		}
+	}
+
+	if (op2_info & MAY_BE_STRING) {
+		|3:
+		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
+			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_STRING))
+			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >3, ZREG_TMP1
+		}
+		|	// offset_key = Z_STR_P(dim);
+		|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+		|	// retval = zend_hash_find(ht, offset_key);
+		switch (type) {
+			case BP_JIT_IS:
+				if (opline->op2_type != IS_CONST) {
+					|	ldrb TMP1w, [FCARG2x, #offsetof(zend_string, val)]
+					|	cmp TMP1w, #((uint8_t) ('9'))
+					|	ble >1
+					|.cold_code
+					|1:
+					|	EXT_CALL zend_jit_symtable_find, REG0
+					|	b >1
+					|.code
+					|	EXT_CALL zend_hash_find, REG0
+					|1:
+				} else {
+					|	EXT_CALL _zend_hash_find_known_hash, REG0
+				}
+				|	mov REG0, RETVALx
+				if (not_found_exit_addr) {
+					|	cbz REG0, &not_found_exit_addr
+				} else {
+					|	cbz REG0, >9 // NOT_FOUND
+				}
+				break;
+			case BP_VAR_R:
+			case BP_VAR_IS:
+			case BP_VAR_UNSET:
+				if (opline->op2_type != IS_CONST) {
+					|	ldrb TMP1w, [FCARG2x, #offsetof(zend_string, val)]
+					|	cmp TMP1w, #((uint8_t) ('9'))
+					|	ble >1
+					|.cold_code
+					|1:
+					|	EXT_CALL zend_jit_symtable_find, REG0
+					|	b >1
+					|.code
+					|	EXT_CALL zend_hash_find, REG0
+					|1:
+				} else {
+					|	EXT_CALL _zend_hash_find_known_hash, REG0
+				}
+				|	mov REG0, RETVALx
+				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
+					|	cbz REG0, &exit_addr
+				} else if (type == BP_VAR_IS && not_found_exit_addr) {
+					|	cbz REG0, &not_found_exit_addr
+				} else if (type == BP_VAR_IS && found_exit_addr) {
+					|	cbz REG0, >7
+				} else {
+					|	cbz REG0, >2 // NOT_FOUND
+					|.cold_code
+					|2:
+					switch (type) {
+						case BP_VAR_R:
+							// zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
+							|	UNDEFINED_INDEX opline
+							|	b >9
+							break;
+						case BP_VAR_IS:
+						case BP_VAR_UNSET:
+							|	// retval = &EG(uninitialized_zval);
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+							|	b >9
+							break;
+						default:
+							ZEND_UNREACHABLE();
+					}
+					|.code
+				}
+				break;
+			case BP_VAR_RW:
+				|	SET_EX_OPLINE opline, REG0
+				if (opline->op2_type != IS_CONST) {
+					|	EXT_CALL zend_jit_symtable_lookup_rw, REG0
+				} else {
+					|	EXT_CALL zend_jit_hash_lookup_rw, REG0
+				}
+				|	mov REG0, RETVALx
+				|	cbz REG0, >9
+				break;
+			case BP_VAR_W:
+				if (opline->op2_type != IS_CONST) {
+					|	EXT_CALL zend_jit_symtable_lookup_w, REG0
+				} else {
+					|	EXT_CALL zend_hash_lookup, REG0
+				}
+				|	mov REG0, RETVALx
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+	}
+
+	if (type == BP_JIT_IS && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))) {
+	    |5:
+		if (op1_info & MAY_BE_ARRAY_OF_REF) {
+			|	ZVAL_DEREF REG0, MAY_BE_REF, TMP1w
+		}
+		|	ldrb TMP1w, [REG0,#offsetof(zval, u1.v.type)]
+		|	cmp TMP1w, #IS_NULL
+		if (not_found_exit_addr) {
+			|	ble &not_found_exit_addr
+		} else if (found_exit_addr) {
+			|	bgt &found_exit_addr
+		} else {
+			|	ble >9 // NOT FOUND
+		}
+	}
+
+	if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
+		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
+			|.cold_code
+			|3:
+		}
+		|	SET_EX_OPLINE opline, REG0
+		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		switch (type) {
+			case BP_VAR_R:
+				|   LOAD_ZVAL_ADDR CARG3, res_addr
+				|	EXT_CALL zend_jit_fetch_dim_r_helper, REG0
+				|	mov REG0, RETVALx
+				|	b >9
+				break;
+			case BP_JIT_IS:
+				|	EXT_CALL zend_jit_fetch_dim_isset_helper, REG0
+				|	mov REG0, RETVALx
+				if (not_found_exit_addr) {
+					|	cbz REG0, &not_found_exit_addr
+					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
+						|	b >8
+					}
+				} else if (found_exit_addr) {
+					|	cbnz REG0, &found_exit_addr
+					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
+						|	b >9
+					}
+				} else {
+					|	cbnz REG0, >8
+					|	b >9
+				}
+				break;
+			case BP_VAR_IS:
+			case BP_VAR_UNSET:
+				|   LOAD_ZVAL_ADDR CARG3, res_addr
+				|	EXT_CALL zend_jit_fetch_dim_is_helper, REG0
+				|	mov REG0, RETVALx
+				|	b >9
+				break;
+			case BP_VAR_RW:
+				|	EXT_CALL zend_jit_fetch_dim_rw_helper, REG0
+				|	mov REG0, RETVALx
+				|	cbnz REG0, >8
+				|	b >9
+				break;
+			case BP_VAR_W:
+				|	EXT_CALL zend_jit_fetch_dim_w_helper, REG0
+				|	mov REG0, RETVALx
+				|	cbnz REG0, >8
+				|	b >9
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
+			|.code
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_simple_assign(dasm_State    **Dst,
+                                  const zend_op  *opline,
+                                  zend_jit_addr   var_addr,
+                                  uint32_t        var_info,
+                                  uint32_t        var_def_info,
+                                  zend_uchar      val_type,
+                                  zend_jit_addr   val_addr,
+                                  uint32_t        val_info,
+                                  zend_jit_addr   res_addr,
+                                  int             in_cold,
+                                  int             save_r1)
+/* Labels: 1,2,3 */
+{
+	zend_reg tmp_reg;
+
+	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_addr) != ZREG_REG0) {
+		tmp_reg = ZREG_REG0;
+	} else {
+		/* ASSIGN_DIM */
+		tmp_reg = ZREG_FCARG1x;
+	}
+
+	if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
+		zval *zv = Z_ZV(val_addr);
+
+		if (!res_addr) {
+			|	ZVAL_COPY_CONST var_addr, var_info, var_def_info, zv, tmp_reg, ZREG_TMP1, ZREG_FPR0
+		} else {
+			|	ZVAL_COPY_CONST_2 var_addr, res_addr, var_info, var_def_info, zv, tmp_reg, ZREG_TMP1, ZREG_FPR0
+		}
+		if (Z_REFCOUNTED_P(zv)) {
+			if (!res_addr) {
+				|	ADDREF_CONST zv, TMP1, TMP2
+			} else {
+				|	ADDREF_CONST_2 zv, TMP1, TMP2
+			}
+		}
+	} else {
+		if (val_info & MAY_BE_UNDEF) {
+			if (in_cold) {
+				|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >2, ZREG_TMP1
+			} else {
+				|	IF_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1
+				|.cold_code
+				|1:
+			}
+			|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+			if (save_r1) {
+				|	str FCARG1x, T1	// save
+			}
+			|	SET_ZVAL_TYPE_INFO var_addr, IS_NULL, TMP1w, TMP2
+			if (res_addr) {
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+			}
+			if (opline) {
+				|	SET_EX_OPLINE opline, Rx(tmp_reg)
+			}
+			ZEND_ASSERT(Z_MODE(val_addr) == IS_MEM_ZVAL && Z_REG(val_addr) == ZREG_FP);
+			|	LOAD_32BIT_VAL FCARG1w, Z_OFFSET(val_addr)
+			|	EXT_CALL zend_jit_undefined_op_helper, REG0
+			if (save_r1) {
+				|	ldr FCARG1x, T1	// restore
+			}
+			|	b >3
+			if (in_cold) {
+				|2:
+			} else {
+				|.code
+			}
+		}
+		if (val_info & MAY_BE_REF) {
+			if (val_type == IS_CV) {
+				ZEND_ASSERT(Z_REG(var_addr) != ZREG_REG2);
+				if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_REG2 || Z_OFFSET(val_addr) != 0) {
+					|	LOAD_ZVAL_ADDR REG2, val_addr
+				}
+				|	ZVAL_DEREF REG2, val_info, TMP1w
+				val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, 0);
+			} else {
+				zend_jit_addr ref_addr;
+
+				if (in_cold) {
+					|	IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, >1, ZREG_TMP1
+				} else {
+					|	IF_ZVAL_TYPE val_addr, IS_REFERENCE, >1, ZREG_TMP1
+					|.cold_code
+					|1:
+				}
+				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
+				|	GET_ZVAL_PTR REG2, val_addr, TMP1
+				|	GC_DELREF REG2, TMP1w
+				|	// ZVAL_COPY_VALUE(return_value, &ref->val);
+				ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, offsetof(zend_reference, val));
+				if (!res_addr) {
+					|	ZVAL_COPY_VALUE var_addr, var_info, ref_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+				} else {
+					|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, ref_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+				}
+				|	beq >2 // GC_DELREF() reached zero
+				|	IF_NOT_REFCOUNTED REG2w, >3, TMP1w
+				if (!res_addr) {
+					|	GC_ADDREF Rx(tmp_reg), TMP1w
+				} else {
+					|	GC_ADDREF_2 Rx(tmp_reg), TMP1w
+				}
+				|	b >3
+				|2:
+				if (res_addr) {
+					|	IF_NOT_REFCOUNTED REG2w, >2, TMP1w
+					|	GC_ADDREF Rx(tmp_reg), TMP1w
+					|2:
+				}
+				if (save_r1) {
+					|	str FCARG1x, T1 // save
+				}
+				|	GET_ZVAL_PTR FCARG1x, val_addr, TMP1
+				|	EFREE_REFERENCE
+				if (save_r1) {
+					|	ldr FCARG1x, T1 // restore
+				}
+				|	b >3
+				if (in_cold) {
+					|1:
+				} else {
+					|.code
+				}
+			}
+		}
+
+		if (!res_addr) {
+			|	ZVAL_COPY_VALUE var_addr, var_info, val_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		} else {
+			|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, val_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		}
+
+		if (val_type == IS_CV) {
+			if (!res_addr) {
+				|	TRY_ADDREF val_info, REG2w, Rx(tmp_reg), TMP1w
+			} else {
+				|	TRY_ADDREF_2 val_info, REG2w, Rx(tmp_reg), TMP1w
+			}
+		} else {
+			if (res_addr) {
+				|	TRY_ADDREF val_info, REG2w, Rx(tmp_reg), TMP1w
+			}
+		}
+		|3:
+	}
+	return 1;
+}
+
+static int zend_jit_assign_to_typed_ref(dasm_State         **Dst,
+                                       const zend_op        *opline,
+                                       zend_uchar            val_type,
+                                       zend_jit_addr         val_addr,
+                                       bool                  check_exception)
+{
+	|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
+	|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
+	|	cbnz TMP1, >2
+	|.cold_code
+	|2:
+	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2x || Z_OFFSET(val_addr) != 0) {
+		|	LOAD_ZVAL_ADDR FCARG2x, val_addr
+	}
+	if (opline) {
+		|	SET_EX_OPLINE opline, REG0
+	}
+	if (val_type == IS_CONST) {
+		|	EXT_CALL zend_jit_assign_const_to_typed_ref, REG0
+	} else if (val_type == IS_TMP_VAR) {
+		|	EXT_CALL zend_jit_assign_tmp_to_typed_ref, REG0
+	} else if (val_type == IS_VAR) {
+		|	EXT_CALL zend_jit_assign_var_to_typed_ref, REG0
+	} else if (val_type == IS_CV) {
+		|	EXT_CALL zend_jit_assign_cv_to_typed_ref, REG0
+	} else {
+		ZEND_UNREACHABLE();
+	}
+	if (check_exception) {
+		|	// if (UNEXPECTED(EG(exception) != NULL)) {
+		|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+		|	cbz REG0, >8  // END OF zend_jit_assign_to_variable()
+		|	b ->exception_handler
+	} else {
+		|	b >8
+	}
+	|.code
+
+	return 1;
+}
+
+static int zend_jit_assign_to_variable_call(dasm_State    **Dst,
+                                            const zend_op  *opline,
+                                            zend_jit_addr   __var_use_addr,
+                                            zend_jit_addr   var_addr,
+                                            uint32_t        __var_info,
+                                            uint32_t        __var_def_info,
+                                            zend_uchar      val_type,
+                                            zend_jit_addr   val_addr,
+                                            uint32_t        val_info,
+                                            zend_jit_addr   __res_addr,
+                                            bool            __check_exception)
+{
+	if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1x || Z_OFFSET(var_addr) != 0) {
+		|	LOAD_ZVAL_ADDR FCARG1x, var_addr
+	}
+	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2x || Z_OFFSET(val_addr) != 0) {
+		|	LOAD_ZVAL_ADDR FCARG2x, val_addr
+	}
+	if (opline) {
+		|	SET_EX_OPLINE opline, REG0
+	}
+	if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+		|	bl ->assign_tmp
+	} else if (val_type == IS_CONST) {
+		|	bl ->assign_const
+	} else if (val_type == IS_TMP_VAR) {
+		|	bl ->assign_tmp
+	} else if (val_type == IS_VAR) {
+		if (!(val_info & MAY_BE_REF)) {
+			|	bl ->assign_tmp
+		} else {
+			|	bl ->assign_var
+		}
+	} else if (val_type == IS_CV) {
+		if (!(val_info & MAY_BE_REF)) {
+			|	bl ->assign_cv_noref
+		} else {
+			|	bl ->assign_cv
+		}
+	} else {
+		ZEND_UNREACHABLE();
+	}
+
+	return 1;
+}
+
+static int zend_jit_assign_to_variable(dasm_State    **Dst,
+                                       const zend_op  *opline,
+                                       zend_jit_addr   var_use_addr,
+                                       zend_jit_addr   var_addr,
+                                       uint32_t        var_info,
+                                       uint32_t        var_def_info,
+                                       zend_uchar      val_type,
+                                       zend_jit_addr   val_addr,
+                                       uint32_t        val_info,
+                                       zend_jit_addr   res_addr,
+                                       bool            check_exception)
+/* Labels: 1,2,3,4,5,8 */
+{
+	int done = 0;
+	zend_reg ref_reg, tmp_reg;
+
+	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_use_addr) != ZREG_REG0) {
+		ref_reg = ZREG_FCARG1x;
+		tmp_reg = ZREG_REG0;
+	} else {
+		/* ASSIGN_DIM */
+		ref_reg = ZREG_REG0;
+		tmp_reg = ZREG_FCARG1x;
+	}
+
+	if (var_info & MAY_BE_REF) {
+		if (Z_MODE(var_use_addr) != IS_MEM_ZVAL || Z_REG(var_use_addr) != ref_reg || Z_OFFSET(var_use_addr) != 0) {
+			|	LOAD_ZVAL_ADDR Rx(ref_reg), var_use_addr
+			var_addr = var_use_addr = ZEND_ADDR_MEM_ZVAL(ref_reg, 0);
+		}
+		|	// if (Z_ISREF_P(variable_ptr)) {
+		|	IF_NOT_Z_TYPE Rx(ref_reg), IS_REFERENCE, >1, TMP1w
+		|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
+		|	GET_Z_PTR FCARG1x, Rx(ref_reg)
+		if (!zend_jit_assign_to_typed_ref(Dst, opline, val_type, val_addr, check_exception)) {
+			return 0;
+		}
+		|	add Rx(ref_reg), FCARG1x, #offsetof(zend_reference, val)
+		|1:
+	}
+	if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+		if (RC_MAY_BE_1(var_info)) {
+			int in_cold = 0;
+
+			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+				|	IF_ZVAL_REFCOUNTED var_use_addr, >1, ZREG_TMP1, ZREG_TMP2
+				|.cold_code
+				|1:
+				in_cold = 1;
+			}
+			if (Z_REG(var_use_addr) == ZREG_FCARG1x || Z_REG(var_use_addr) == ZREG_REG0) {
+				bool keep_gc = 0;
+
+				|	GET_ZVAL_PTR Rx(tmp_reg), var_use_addr, TMP1
+#if 0
+				// TODO: This optiization doesn't work on ARM
+				if (tmp_reg == ZREG_FCARG1x) {
+					if (Z_MODE(val_addr) == IS_REG) {
+						keep_gc = 1;
+					} else if ((val_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) == 0) {
+						keep_gc = 1;
+					} else if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
+						zval *zv = Z_ZV(val_addr);
+						if (Z_TYPE_P(zv) == IS_DOUBLE) {
+							if (Z_DVAL_P(zv) == 0) {
+								keep_gc = 1;
+							}
+						} else if (IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
+							keep_gc = 1;
+						}
+					} else if (Z_MODE(val_addr) == IS_MEM_ZVAL) {
+						if ((val_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
+							keep_gc = 1;
+						}
+					}
+				}
+#endif
+				if (!keep_gc) {
+					|	str Rx(tmp_reg), T1 // save
+				}
+				if (!zend_jit_simple_assign(Dst, opline, var_addr, var_info, var_def_info, val_type, val_addr, val_info, res_addr, in_cold, 0)) {
+					return 0;
+				}
+				if (!keep_gc) {
+					|	ldr FCARG1x, T1     // restore
+				}
+			} else {
+				|	GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1
+				if (!zend_jit_simple_assign(Dst, opline, var_addr, var_info, var_def_info, val_type, val_addr, val_info, res_addr, in_cold, 1)) {
+					return 0;
+				}
+			}
+			|	GC_DELREF FCARG1x, TMP1w
+			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
+				|	bne >4
+			} else {
+				|	bne >8
+			}
+			|	ZVAL_DTOR_FUNC var_info, opline, TMP1
+			if (in_cold || (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0)) {
+				if (check_exception) {
+					|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+					|	cbz REG0, >8
+					|	b ->exception_handler
+				} else {
+					|	b >8
+				}
+			}
+			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
+				|4:
+				|	IF_GC_MAY_NOT_LEAK FCARG1x, >8, TMP1w, TMP2w
+				|	EXT_CALL gc_possible_root, REG0
+				if (in_cold) {
+					|	b >8
+				}
+			}
+			if (in_cold) {
+				|.code
+			} else {
+				done = 1;
+			}
+		} else /* if (RC_MAY_BE_N(var_info)) */ {
+			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+				|	IF_NOT_ZVAL_REFCOUNTED var_use_addr, >5, ZREG_TMP1, ZREG_TMP2
+			}
+			if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
+				if (Z_REG(var_use_addr) == ZREG_FP) {
+					|	str Rx(Z_REG(var_use_addr)), T1 // save
+				}
+				|	GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1
+				|	GC_DELREF FCARG1x, TMP1w
+				|	IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w
+				|	EXT_CALL gc_possible_root, TMP1
+				if (Z_REG(var_use_addr) != ZREG_FP) {
+					|	ldr Rx(Z_REG(var_use_addr)), T1 // restore
+				}
+			} else {
+				|	GET_ZVAL_PTR Rx(tmp_reg), var_use_addr, TMP1
+				|	GC_DELREF Rx(tmp_reg), TMP1w
+			}
+			|5:
+	    }
+	}
+
+	if (!done && !zend_jit_simple_assign(Dst, opline, var_addr, var_info, var_def_info, val_type, val_addr, val_info, res_addr, 0, 0)) {
+		return 0;
+	}
+
+	|8:
+
+	return 1;
+}
+
+static int zend_jit_assign_dim(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, uint32_t val_info, int may_throw)
+{
+	zend_jit_addr op2_addr, op3_addr, res_addr;
+
+	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
+	op3_addr = OP1_DATA_ADDR();
+	if (opline->result_type == IS_UNUSED) {
+		res_addr = 0;
+	} else {
+		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+	}
+
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (val_info & MAY_BE_UNDEF)) {
+		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+		if (!exit_addr) {
+			return 0;
+		}
+
+		|	IF_ZVAL_TYPE op3_addr, IS_UNDEF, &exit_addr, ZREG_TMP1
+
+		val_info &= ~MAY_BE_UNDEF;
+	}
+
+	if (op1_info & MAY_BE_REF) {
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w
+		|	GET_Z_PTR FCARG2x, FCARG1x
+		|	ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))]
+		|	cmp TMP1w, #IS_ARRAY
+		|	bne >2
+		|	add FCARG1x, FCARG2x, #offsetof(zend_reference, val)
+		|	b >3
+		|.cold_code
+		|2:
+		|	SET_EX_OPLINE opline, REG0
+		|	EXT_CALL zend_jit_prepare_assign_dim_ref, REG0
+		|	mov FCARG1x, RETVALx
+		|	cbnz FCARG1x, >1
+		|	b ->exception_handler_undef
+		|.code
+		|1:
+		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	}
+
+	if (op1_info & MAY_BE_ARRAY) {
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
+		}
+		|3:
+		|	SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2
+	} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+			|	CMP_ZVAL_TYPE op1_addr, IS_FALSE, ZREG_TMP1
+			|	bgt >7
+		}
+		|	// ZVAL_ARR(container, zend_new_array(8));
+		if (Z_REG(op1_addr) != ZREG_FP) {
+			|	str Rx(Z_REG(op1_addr)), T1 // save
+		}
+		|	EXT_CALL _zend_new_array_0, REG0
+		|	mov REG0, RETVALx
+		if (Z_REG(op1_addr) != ZREG_FP) {
+			|	ldr Rx(Z_REG(op1_addr)), T1 // restore
+		}
+		|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
+		|	mov FCARG1x, REG0
+	}
+
+	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+		|6:
+		if (opline->op2_type == IS_UNUSED) {
+			uint32_t var_info = MAY_BE_NULL;
+			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+
+			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
+			|	EXT_CALL zend_hash_next_index_insert, REG0
+			|	// if (UNEXPECTED(!var_ptr)) {
+			|	mov REG0, RETVALx
+			|	cbz REG0, >1
+			|.cold_code
+			|1:
+			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
+			|	CANNOT_ADD_ELEMENT opline
+			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
+			|	b >9
+			|.code
+
+			if (!zend_jit_simple_assign(Dst, opline, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0, 0)) {
+				return 0;
+			}
+		} else {
+			uint32_t var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
+			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+
+			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_W, op1_info, op2_info, NULL, NULL, NULL)) {
+				return 0;
+			}
+
+			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
+				var_info |= MAY_BE_REF;
+			}
+			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+				var_info |= MAY_BE_RC1;
+			}
+
+			|8:
+			|	// value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE);
+			if (opline->op1_type == IS_VAR) {
+				ZEND_ASSERT(opline->result_type == IS_UNUSED);
+				if (!zend_jit_assign_to_variable_call(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) {
+					return 0;
+				}
+			} else {
+				if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) {
+					return 0;
+				}
+			}
+		}
+	}
+
+	if (((op1_info & MAY_BE_ARRAY) &&
+	     (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE))) ||
+	    (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)))) {
+		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+			|.cold_code
+			|7:
+		}
+
+		if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) &&
+		    (op1_info & MAY_BE_ARRAY)) {
+			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+				|	CMP_ZVAL_TYPE op1_addr, IS_FALSE, ZREG_TMP1
+				|	bgt >2
+			}
+			|	// ZVAL_ARR(container, zend_new_array(8));
+			if (Z_REG(op1_addr) != ZREG_FP) {
+				|	str Rx(Z_REG(op1_addr)), T1 // save
+			}
+			|	EXT_CALL _zend_new_array_0, REG0
+			|	mov REG0, RETVALx
+			if (Z_REG(op1_addr) != ZREG_FP) {
+				|	ldr Rx(Z_REG(op1_addr)), T1 // restore
+			}
+			|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
+			|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
+			|	mov FCARG1x, REG0
+			|	// ZEND_VM_C_GOTO(assign_dim_op_new_array);
+			|	b <6
+			|2:
+		}
+
+		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+			|	SET_EX_OPLINE opline, REG0
+		    if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			}
+		    if (opline->op2_type == IS_UNUSED) {
+				|	mov FCARG2x, xzr
+			} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
+				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
+				|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
+			} else {
+				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+			}
+			if (opline->result_type == IS_UNUSED) {
+				|	mov CARG4, xzr
+			} else {
+				|	LOAD_ZVAL_ADDR CARG4, res_addr
+			}
+			|	LOAD_ZVAL_ADDR CARG3, op3_addr
+			|	EXT_CALL zend_jit_assign_dim_helper, REG0
+
+#ifdef ZEND_JIT_USE_RC_INFERENCE
+			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR)) && (val_info & MAY_BE_RC1)) {
+				/* ASSIGN_DIM may increase refcount of the value */
+				val_info |= MAY_BE_RCN;
+			}
+#endif
+
+			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		}
+
+		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+				|	b >9 // END
+			}
+			|.code
+		}
+	}
+
+#ifdef ZEND_JIT_USE_RC_INFERENCE
+	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+		/* ASSIGN_DIM may increase refcount of the key */
+		op2_info |= MAY_BE_RCN;
+	}
+#endif
+
+	|9:
+	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+
+	if (may_throw) {
+		zend_jit_check_exception(Dst);
+	}
+
+	return 1;
+}
+
+static int zend_jit_assign_dim_op(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint32_t op1_def_info, zend_jit_addr op1_addr, uint32_t op2_info, uint32_t op1_data_info, zend_ssa_range *op1_data_range, int may_throw)
+{
+	zend_jit_addr op2_addr, op3_addr, var_addr;
+
+	ZEND_ASSERT(opline->result_type == IS_UNUSED);
+
+	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
+	op3_addr = OP1_DATA_ADDR();
+
+	if (op1_info & MAY_BE_REF) {
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w
+		|	GET_Z_PTR FCARG2x, FCARG1x
+		|	ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))]
+		|	cmp TMP1w, #IS_ARRAY
+		|	bne >2
+		|	add FCARG1x, FCARG2x, #offsetof(zend_reference, val)
+		|	b >3
+		|.cold_code
+		|2:
+		|	SET_EX_OPLINE opline, REG0
+		|	EXT_CALL zend_jit_prepare_assign_dim_ref, REG0
+		|	mov FCARG1x, RETVALx
+		|	cbnz RETVALx, >1
+		|	b ->exception_handler_undef
+		|.code
+		|1:
+		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	}
+
+	if (op1_info & MAY_BE_ARRAY) {
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
+		}
+		|3:
+		|	SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2
+	}
+	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+		if (op1_info & MAY_BE_ARRAY) {
+			|.cold_code
+			|7:
+		}
+		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+			|	CMP_ZVAL_TYPE op1_addr, IS_FALSE, ZREG_TMP1
+			|	bgt >7
+		}
+		if (op1_info & MAY_BE_UNDEF) {
+			if (op1_info & (MAY_BE_NULL|MAY_BE_FALSE)) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+			}
+			|	SET_EX_OPLINE opline, REG0
+			|	LOAD_32BIT_VAL FCARG1x, opline->op1.var
+			|	EXT_CALL zend_jit_undefined_op_helper, REG0
+			|1:
+		}
+		|	// ZVAL_ARR(container, zend_new_array(8));
+		if (Z_REG(op1_addr) != ZREG_FP) {
+			|	str Rx(Z_REG(op1_addr)), T1 // save
+		}
+		|	EXT_CALL _zend_new_array_0, REG0
+		|	mov REG0, RETVALx
+		if (Z_REG(op1_addr) != ZREG_FP) {
+			|	ldr Rx(Z_REG(op1_addr)), T1 // restore
+		}
+		|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
+		|	mov FCARG1x, REG0
+		if (op1_info & MAY_BE_ARRAY) {
+			|	b >1
+			|.code
+			|1:
+		}
+	}
+
+	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+		uint32_t var_info;
+		uint32_t var_def_info = zend_array_element_type(op1_def_info, opline->op1_type, 1, 0);
+
+		|6:
+		if (opline->op2_type == IS_UNUSED) {
+			var_info = MAY_BE_NULL;
+
+			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
+			|	EXT_CALL zend_hash_next_index_insert, REG0
+			|	mov REG0, RETVALx
+			|	// if (UNEXPECTED(!var_ptr)) {
+			|	cbz REG0, >1
+			|.cold_code
+			|1:
+			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
+			|	CANNOT_ADD_ELEMENT opline
+			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
+			|	b >9
+			|.code
+		} else {
+			var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
+			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
+				var_info |= MAY_BE_REF;
+			}
+			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+				var_info |= MAY_BE_RC1;
+			}
+
+			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, NULL, NULL, NULL)) {
+				return 0;
+			}
+
+			|8:
+			if (op1_info & (MAY_BE_ARRAY_OF_REF)) {
+				binary_op_type binary_op = get_binary_op(opline->extended_value);
+				|	IF_NOT_Z_TYPE, REG0, IS_REFERENCE, >1, TMP1w
+				|	GET_Z_PTR FCARG1x, REG0
+				|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
+				|	cbnz TMP1, >2
+				|	add REG0, FCARG1x, #offsetof(zend_reference, val)
+				|.cold_code
+				|2:
+				|	LOAD_ZVAL_ADDR FCARG2x, op3_addr
+				|	LOAD_ADDR CARG3, binary_op
+				|	SET_EX_OPLINE opline, REG0
+				|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
+				zend_jit_check_exception(Dst);
+				|	b >9
+				|.code
+				|1:
+			}
+		}
+
+		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+		switch (opline->extended_value) {
+			case ZEND_ADD:
+			case ZEND_SUB:
+			case ZEND_MUL:
+			case ZEND_DIV:
+				if (!zend_jit_math_helper(Dst, opline, opline->extended_value, IS_CV, opline->op1, var_addr, var_info, (opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info, 0, var_addr, var_def_info, var_info,
+						1 /* may overflow */, may_throw)) {
+					return 0;
+				}
+				break;
+			case ZEND_BW_OR:
+			case ZEND_BW_AND:
+			case ZEND_BW_XOR:
+			case ZEND_SL:
+			case ZEND_SR:
+			case ZEND_MOD:
+				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
+						IS_CV, opline->op1, var_addr, var_info, NULL,
+						(opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info,
+						op1_data_range,
+						0, var_addr, var_def_info, var_info, may_throw)) {
+					return 0;
+				}
+				break;
+			case ZEND_CONCAT:
+				if (!zend_jit_concat_helper(Dst, opline, IS_CV, opline->op1, var_addr, var_info, (opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info, var_addr,
+						may_throw)) {
+					return 0;
+				}
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+	}
+
+	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+		binary_op_type binary_op;
+
+		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+			|.cold_code
+			|7:
+		}
+
+		|	SET_EX_OPLINE opline, REG0
+		if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		}
+	    if (opline->op2_type == IS_UNUSED) {
+			|	mov FCARG2x, xzr
+		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
+			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
+			|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
+		} else {
+			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		}
+		binary_op = get_binary_op(opline->extended_value);
+		|	LOAD_ZVAL_ADDR CARG3, op3_addr
+		|	LOAD_ADDR CARG4, binary_op
+		|	EXT_CALL zend_jit_assign_dim_op_helper, REG0
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+
+		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+			|	b >9 // END
+			|.code
+		}
+	}
+
+	|9:
+	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+
+	return 1;
+}
+
+static int zend_jit_assign_op(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint32_t op1_def_info, zend_ssa_range *op1_range, uint32_t op2_info, zend_ssa_range *op2_range, int may_overflow, int may_throw)
+{
+	zend_jit_addr op1_addr, op2_addr;
+
+	ZEND_ASSERT(opline->op1_type == IS_CV && opline->result_type == IS_UNUSED);
+	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
+
+	op1_addr = OP1_ADDR();
+	op2_addr = OP2_ADDR();
+
+	if (op1_info & MAY_BE_REF) {
+		binary_op_type binary_op = get_binary_op(opline->extended_value);
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	IF_NOT_Z_TYPE, FCARG1x, IS_REFERENCE, >1, TMP1w
+		|	GET_Z_PTR FCARG1x, FCARG1x
+		|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
+		|	cbnz TMP1, >2
+		|	add FCARG1x, FCARG1x, #offsetof(zend_reference, val)
+		|.cold_code
+		|2:
+		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		|	LOAD_ADDR CARG3, binary_op
+		|	SET_EX_OPLINE opline, REG0
+		|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
+		zend_jit_check_exception(Dst);
+		|	b >9
+		|.code
+		|1:
+		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	}
+
+	int result;
+	switch (opline->extended_value) {
+		case ZEND_ADD:
+		case ZEND_SUB:
+		case ZEND_MUL:
+		case ZEND_DIV:
+			result = zend_jit_math_helper(Dst, opline, opline->extended_value, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, opline->op1.var, op1_addr, op1_def_info, op1_info, may_overflow, may_throw);
+			break;
+		case ZEND_BW_OR:
+		case ZEND_BW_AND:
+		case ZEND_BW_XOR:
+		case ZEND_SL:
+		case ZEND_SR:
+		case ZEND_MOD:
+			result = zend_jit_long_math_helper(Dst, opline, opline->extended_value,
+				opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
+				opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
+				opline->op1.var, op1_addr, op1_def_info, op1_info, may_throw);
+			break;
+		case ZEND_CONCAT:
+			result = zend_jit_concat_helper(Dst, opline, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, op1_addr, may_throw);
+			break;
+		default:
+			ZEND_UNREACHABLE();
+	}
+	|9:
+	return result;
+}
+
+static int zend_jit_is_constant_cmp_long_long(const zend_op  *opline,
+                                              zend_ssa_range *op1_range,
+                                              zend_jit_addr   op1_addr,
+                                              zend_ssa_range *op2_range,
+                                              zend_jit_addr   op2_addr,
+                                              bool           *result)
+{
+	zend_long op1_min;
+	zend_long op1_max;
+	zend_long op2_min;
+	zend_long op2_max;
+
+	if (op1_range) {
+		op1_min = op1_range->min;
+		op1_max = op1_range->max;
+	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
+		ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG);
+		op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr));
+	} else {
+		return 0;
+	}
+
+	if (op2_range) {
+		op2_min = op2_range->min;
+		op2_max = op2_range->max;
+	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+		ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG);
+		op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr));
+	} else {
+		return 0;
+	}
+
+	switch (opline->opcode) {
+		case ZEND_IS_EQUAL:
+		case ZEND_IS_IDENTICAL:
+		case ZEND_CASE:
+		case ZEND_CASE_STRICT:
+			if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
+				*result = 1;
+				return 1;
+			} else if (op1_max < op2_min || op1_min > op2_max) {
+				*result = 0;
+				return 1;
+			}
+			return 0;
+		case ZEND_IS_NOT_EQUAL:
+		case ZEND_IS_NOT_IDENTICAL:
+			if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
+				*result = 0;
+				return 1;
+			} else if (op1_max < op2_min || op1_min > op2_max) {
+				*result = 1;
+				return 1;
+			}
+			return 0;
+		case ZEND_IS_SMALLER:
+			if (op1_max < op2_min) {
+				*result = 1;
+				return 1;
+			} else if (op1_min >= op2_max) {
+				*result = 0;
+				return 1;
+			}
+			return 0;
+		case ZEND_IS_SMALLER_OR_EQUAL:
+			if (op1_max <= op2_min) {
+				*result = 1;
+				return 1;
+			} else if (op1_min > op2_max) {
+				*result = 0;
+				return 1;
+			}
+			return 0;
+		default:
+			ZEND_UNREACHABLE();
+	}
+	return 0;
+}
+
+static int zend_jit_cmp_long_long(dasm_State    **Dst,
+                                  const zend_op  *opline,
+                                  zend_ssa_range *op1_range,
+                                  zend_jit_addr   op1_addr,
+                                  zend_ssa_range *op2_range,
+                                  zend_jit_addr   op2_addr,
+                                  zend_jit_addr   res_addr,
+                                  zend_uchar      smart_branch_opcode,
+                                  uint32_t        target_label,
+                                  uint32_t        target_label2,
+                                  const void     *exit_addr,
+                                  bool            skip_comparison)
+{
+	bool swap = 0;
+	bool result;
+
+	if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) {
+		if (!smart_branch_opcode ||
+		    smart_branch_opcode == ZEND_JMPZ_EX ||
+		    smart_branch_opcode == ZEND_JMPNZ_EX) {
+			|	SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE), TMP1w, TMP2
+		}
+		if (smart_branch_opcode && !exit_addr) {
+			if (smart_branch_opcode == ZEND_JMPZ ||
+			    smart_branch_opcode == ZEND_JMPZ_EX) {
+				if (!result) {
+					| b => target_label
+				}
+			} else if (smart_branch_opcode == ZEND_JMPNZ ||
+			           smart_branch_opcode == ZEND_JMPNZ_EX) {
+				if (result) {
+					| b => target_label
+				}
+			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+				if (!result) {
+					| b => target_label
+				} else {
+					| b => target_label2
+				}
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		}
+		return 1;
+	}
+
+	if (skip_comparison) {
+		if (Z_MODE(op1_addr) != IS_REG &&
+		    (Z_MODE(op2_addr) == IS_REG ||
+		     (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL))) {
+			swap = 1;
+		}
+	} else if (Z_MODE(op1_addr) == IS_REG) {
+		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
+			|	cmp Rx(Z_REG(op1_addr)), xzr
+		} else {
+			|	LONG_CMP Z_REG(op1_addr), op2_addr, TMP1
+		}
+	} else if (Z_MODE(op2_addr) == IS_REG) {
+		if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 0) {
+			|	cmp Rx(Z_REG(op2_addr)), xzr
+		} else {
+			|	LONG_CMP Z_REG(op2_addr), op1_addr, TMP1
+		}
+		swap = 1;
+	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL) {
+		|	LONG_CMP_WITH_CONST op2_addr, Z_LVAL_P(Z_ZV(op1_addr)), TMP1, TMP2
+		swap = 1;
+	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_MODE(op1_addr) != IS_CONST_ZVAL) {
+		|	LONG_CMP_WITH_CONST op1_addr, Z_LVAL_P(Z_ZV(op2_addr)), TMP1, TMP2
+	} else {
+		|	GET_ZVAL_LVAL ZREG_REG0, op1_addr, TMP1
+		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
+			|	cmp Rx(ZREG_REG0), xzr
+		} else {
+			|	LONG_CMP ZREG_REG0, op2_addr, TMP1
+		}
+	}
+
+	if (smart_branch_opcode) {
+		if (smart_branch_opcode == ZEND_JMPZ_EX ||
+		    smart_branch_opcode == ZEND_JMPNZ_EX) {
+
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					|	cset REG0w, eq
+					break;
+				case ZEND_IS_NOT_EQUAL:
+				case ZEND_IS_NOT_IDENTICAL:
+					|	cset REG0w, ne
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						|	cset REG0w, gt
+					} else {
+						|	cset REG0w, lt
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						|	cset REG0w, ge
+					} else {
+						|	cset REG0w, le
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+			|	add REG0w, REG0w, #2
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+		}
+		if (smart_branch_opcode == ZEND_JMPZ ||
+		    smart_branch_opcode == ZEND_JMPZ_EX) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					if (exit_addr) {
+						| bne &exit_addr
+					} else {
+						| bne => target_label
+					}
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					if (exit_addr) {
+						| beq &exit_addr
+					} else {
+						| beq => target_label
+					}
+					break;
+				case ZEND_IS_NOT_IDENTICAL:
+					if (exit_addr) {
+						| bne &exit_addr
+					} else {
+						| beq => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						if (exit_addr) {
+							| ble &exit_addr
+						} else {
+							| ble => target_label
+						}
+					} else {
+						if (exit_addr) {
+							| bge &exit_addr
+						} else {
+							| bge => target_label
+						}
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						if (exit_addr) {
+							| blt &exit_addr
+						} else {
+							| blt => target_label
+						}
+					} else {
+						if (exit_addr) {
+							| bgt &exit_addr
+						} else {
+							| bgt => target_label
+						}
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else if (smart_branch_opcode == ZEND_JMPNZ ||
+		           smart_branch_opcode == ZEND_JMPNZ_EX) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					if (exit_addr) {
+						| beq &exit_addr
+					} else {
+						| beq => target_label
+					}
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					if (exit_addr) {
+						| bne &exit_addr
+					} else {
+						| bne => target_label
+					}
+					break;
+				case ZEND_IS_NOT_IDENTICAL:
+					if (exit_addr) {
+						| beq &exit_addr
+					} else {
+						| bne => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						if (exit_addr) {
+							| bgt &exit_addr
+						} else {
+							| bgt => target_label
+						}
+					} else {
+						if (exit_addr) {
+							| blt &exit_addr
+						} else {
+							| blt => target_label
+						}
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						if (exit_addr) {
+							| bge &exit_addr
+						} else {
+							| bge => target_label
+						}
+					} else {
+						if (exit_addr) {
+							| ble &exit_addr
+						} else {
+							| ble => target_label
+						}
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					| bne => target_label
+					break;
+				case ZEND_IS_NOT_EQUAL:
+				case ZEND_IS_NOT_IDENTICAL:
+					| beq => target_label
+					break;
+				case ZEND_IS_SMALLER:
+				    if (swap) {
+						| ble => target_label
+				    } else {
+						| bge => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						| blt => target_label
+					} else {
+						| bgt => target_label
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+			| b => target_label2
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	} else {
+		switch (opline->opcode) {
+			case ZEND_IS_EQUAL:
+			case ZEND_IS_IDENTICAL:
+			case ZEND_CASE:
+			case ZEND_CASE_STRICT:
+				|	cset REG0w, eq
+				break;
+			case ZEND_IS_NOT_EQUAL:
+			case ZEND_IS_NOT_IDENTICAL:
+				|	cset REG0w, ne
+				break;
+			case ZEND_IS_SMALLER:
+				if (swap) {
+					|	cset REG0w, gt
+				} else {
+					|	cset REG0w, lt
+				}
+				break;
+			case ZEND_IS_SMALLER_OR_EQUAL:
+				if (swap) {
+					|	cset REG0w, ge
+				} else {
+					|	cset REG0w, le
+				}
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+		|	add REG0w, REG0w, #2
+		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+	}
+
+	return 1;
+}
+
+static int zend_jit_cmp_double_common(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, bool swap, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	if (smart_branch_opcode) {
+		if (smart_branch_opcode == ZEND_JMPZ) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					if (exit_addr) {
+						|	bne &exit_addr
+					} else {
+						|	bne => target_label
+					}
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					|	bvs >1
+					if (exit_addr) {
+						|	beq &exit_addr
+					} else {
+						|	beq => target_label
+					}
+					|1:
+					break;
+				case ZEND_IS_NOT_IDENTICAL:
+					if (exit_addr) {
+						| bvs &exit_addr
+						| bne &exit_addr
+					} else {
+						| bvs >1
+						| beq => target_label
+						|1:
+					}
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						if (exit_addr) {
+							|	bvs &exit_addr
+							|	bls &exit_addr
+						} else {
+							|	bvs => target_label
+							|	bls => target_label
+						}
+					} else {
+						if (exit_addr) {
+							|	bhs &exit_addr
+						} else {
+							|	bhs => target_label
+						}
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						if (exit_addr) {
+							|	bvs &exit_addr
+							|	blo &exit_addr
+						} else {
+							|	bvs => target_label
+							|	blo => target_label
+						}
+					} else {
+						if (exit_addr) {
+							|	bhi &exit_addr
+						} else {
+							|	bhi => target_label
+						}
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else if (smart_branch_opcode == ZEND_JMPNZ) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					|	bvs >1
+					if (exit_addr) {
+						|	beq &exit_addr
+					} else {
+						|	beq => target_label
+					}
+					|1:
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					if (exit_addr) {
+						|	bne &exit_addr
+					} else {
+						|	bne => target_label
+					}
+					break;
+				case ZEND_IS_NOT_IDENTICAL:
+					if (exit_addr) {
+						|	bvs >1
+						|	beq &exit_addr
+						|1:
+					} else {
+						|	bne => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						|	bvs >1  // Always False if involving NaN
+						if (exit_addr) {
+							|	bhi &exit_addr
+						} else {
+							|	bhi => target_label
+						}
+						|1:
+					} else {
+						|	bvs >1
+						if (exit_addr) {
+							|	blo	&exit_addr
+						} else {
+							|	blo => target_label
+						}
+						|1:
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						|	bvs >1  // Always False if involving NaN
+						if (exit_addr) {
+							|	bhs &exit_addr
+						} else {
+							|	bhs => target_label
+						}
+						|1:
+					} else {
+						|	bvs >1
+						if (exit_addr) {
+							|	bls &exit_addr
+						} else {
+							|	bls => target_label
+						}
+						|1:
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					| bne => target_label
+					break;
+				case ZEND_IS_NOT_EQUAL:
+				case ZEND_IS_NOT_IDENTICAL:
+					| bvs => target_label2
+					| beq => target_label
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						| bvs => target_label
+						| bls => target_label
+					} else {
+						| bhs => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						| bvs => target_label
+						| blo => target_label
+					} else {
+						| bhi => target_label
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+			| b => target_label2
+		} else if (smart_branch_opcode == ZEND_JMPZ_EX) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					|	bne => target_label
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					break;
+				case ZEND_IS_NOT_EQUAL:
+				case ZEND_IS_NOT_IDENTICAL:
+					|	bvs >1
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					|	beq => target_label
+					|1:
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+						|	bvs => target_label
+						|	bls => target_label
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					} else {
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+						|	bhs => target_label
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+						|	bvs => target_label
+						|	blo => target_label
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					} else {
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+						|	bhi => target_label
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else if (smart_branch_opcode == ZEND_JMPNZ_EX) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_IS_IDENTICAL:
+				case ZEND_CASE:
+				case ZEND_CASE_STRICT:
+					|	bvs >1
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					|	beq => target_label
+					|1:
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					break;
+				case ZEND_IS_NOT_EQUAL:
+				case ZEND_IS_NOT_IDENTICAL:
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					|	bvs => target_label
+					|	bne => target_label
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					break;
+				case ZEND_IS_SMALLER:
+					if (swap) {
+						|	cset REG0w, hi
+						|	add REG0w, REG0w, #2
+						|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+						|	bvs >1  // Always False if involving NaN
+						|	bhi => target_label
+						|1:
+					} else {
+						|	bvs >1
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+						|	blo => target_label
+						|1:
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (swap) {
+						|	cset REG0w, hs
+						|	add REG0w, REG0w, #2
+						|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+						|	bvs >1  // Always False if involving NaN
+						|	bhs => target_label
+						|1:
+					} else {
+						|	bvs >1
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+						|	bls => target_label
+						|1:
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	} else {
+		switch (opline->opcode) {
+			case ZEND_IS_EQUAL:
+			case ZEND_IS_IDENTICAL:
+			case ZEND_CASE:
+			case ZEND_CASE_STRICT:
+				|	bvs >1
+				|	mov REG0, #IS_TRUE
+				|	beq >2
+				|1:
+				|	mov REG0, #IS_FALSE
+				|2:
+				break;
+			case ZEND_IS_NOT_EQUAL:
+			case ZEND_IS_NOT_IDENTICAL:
+				|	bvs >1
+				|	mov REG0, #IS_FALSE
+				|	beq >2
+				|1:
+				|	mov REG0, #IS_TRUE
+				|2:
+				break;
+			case ZEND_IS_SMALLER:
+				|	bvs >1
+				|	mov REG0, #IS_TRUE
+				||	if (swap) {
+				|		bhi >2
+				||	} else {
+				|		blo >2
+				||	}
+				|1:
+				|	mov REG0, #IS_FALSE
+				|2:
+				break;
+			case ZEND_IS_SMALLER_OR_EQUAL:
+				|	bvs >1
+				|	mov REG0, #IS_TRUE
+				||	if (swap) {
+				|		bhs >2
+				||	} else {
+				|		bls >2
+				||	}
+				|1:
+				|	mov REG0, #IS_FALSE
+				|2:
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+	}
+
+	return 1;
+}
+
+static int zend_jit_cmp_long_double(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	zend_reg tmp_reg = ZREG_FPR0;
+
+	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op1_addr, ZREG_REG0, ZREG_TMP1
+	|	DOUBLE_CMP tmp_reg, op2_addr, ZREG_TMP1, ZREG_FPTMP
+
+	return zend_jit_cmp_double_common(Dst, opline, res_addr, 0, smart_branch_opcode, target_label, target_label2, exit_addr);
+}
+
+static int zend_jit_cmp_double_long(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	zend_reg tmp_reg = ZREG_FPR0;
+
+	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, ZREG_REG0, ZREG_TMP1
+	|	DOUBLE_CMP tmp_reg, op1_addr, ZREG_TMP1, ZREG_FPTMP
+
+	return zend_jit_cmp_double_common(Dst, opline, res_addr, /* swap */ 1, smart_branch_opcode, target_label, target_label2, exit_addr);
+}
+
+static int zend_jit_cmp_double_double(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	bool swap = 0;
+
+	if (Z_MODE(op1_addr) == IS_REG) {
+		|	DOUBLE_CMP Z_REG(op1_addr), op2_addr, ZREG_TMP1, ZREG_FPTMP
+	} else if (Z_MODE(op2_addr) == IS_REG) {
+		|	DOUBLE_CMP Z_REG(op2_addr), op1_addr, ZREG_TMP1, ZREG_FPTMP
+		swap = 1;
+	} else {
+		zend_reg tmp_reg = ZREG_FPR0;
+
+		|	GET_ZVAL_DVAL tmp_reg, op1_addr, ZREG_TMP1
+		|	DOUBLE_CMP tmp_reg, op2_addr, ZREG_TMP1, ZREG_FPTMP
+	}
+
+	return zend_jit_cmp_double_common(Dst, opline, res_addr, swap, smart_branch_opcode, target_label, target_label2, exit_addr);
+}
+
+static int zend_jit_cmp_slow(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	|	LONG_CMP_WITH_CONST res_addr, Z_L(0), TMP1, TMP2
+	if (smart_branch_opcode) {
+		if (smart_branch_opcode == ZEND_JMPZ_EX ||
+		    smart_branch_opcode == ZEND_JMPNZ_EX) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_CASE:
+					|	cset REG0w, eq
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					|	cset REG0w, ne
+					break;
+				case ZEND_IS_SMALLER:
+					|	cset REG0w, lt
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					|	cset REG0w, le
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+			|	add REG0w, REG0w, #2
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+		}
+		if (smart_branch_opcode == ZEND_JMPZ ||
+		    smart_branch_opcode == ZEND_JMPZ_EX) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_CASE:
+					if (exit_addr) {
+						|	bne &exit_addr
+					} else {
+						| 	bne => target_label
+					}
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					if (exit_addr) {
+						| beq &exit_addr
+					} else {
+						| beq => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER:
+					if (exit_addr) {
+						| bge &exit_addr
+					} else {
+						| bge => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (exit_addr) {
+						| bgt &exit_addr
+					} else {
+						| bgt => target_label
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else if (smart_branch_opcode == ZEND_JMPNZ ||
+		           smart_branch_opcode == ZEND_JMPNZ_EX) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_CASE:
+					if (exit_addr) {
+						| beq &exit_addr
+					} else {
+						| beq => target_label
+					}
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					if (exit_addr) {
+						| bne &exit_addr
+					} else {
+						| bne => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER:
+					if (exit_addr) {
+						| blt &exit_addr
+					} else {
+						| blt => target_label
+					}
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					if (exit_addr) {
+						| ble &exit_addr
+					} else {
+						| ble => target_label
+					}
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+			switch (opline->opcode) {
+				case ZEND_IS_EQUAL:
+				case ZEND_CASE:
+					| bne => target_label
+					break;
+				case ZEND_IS_NOT_EQUAL:
+					| beq => target_label
+					break;
+				case ZEND_IS_SMALLER:
+					| bge => target_label
+					break;
+				case ZEND_IS_SMALLER_OR_EQUAL:
+					| bgt => target_label
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+			| b => target_label2
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	} else {
+		switch (opline->opcode) {
+			case ZEND_IS_EQUAL:
+			case ZEND_CASE:
+				|	cset REG0w, eq
+				break;
+			case ZEND_IS_NOT_EQUAL:
+				|	cset REG0w, ne
+				break;
+			case ZEND_IS_SMALLER:
+				|	cset REG0w, lt
+				break;
+			case ZEND_IS_SMALLER_OR_EQUAL:
+				|	cset REG0w, le
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+		|	add REG0w, REG0w, #2
+		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+	}
+
+	return 1;
+}
+
+static int zend_jit_cmp(dasm_State    **Dst,
+                        const zend_op  *opline,
+                        uint32_t        op1_info,
+                        zend_ssa_range *op1_range,
+                        zend_jit_addr   op1_addr,
+                        uint32_t        op2_info,
+                        zend_ssa_range *op2_range,
+                        zend_jit_addr   op2_addr,
+                        zend_jit_addr   res_addr,
+                        int             may_throw,
+                        zend_uchar      smart_branch_opcode,
+                        uint32_t        target_label,
+                        uint32_t        target_label2,
+                        const void     *exit_addr,
+                        bool            skip_comparison)
+{
+	bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
+	bool has_slow;
+
+	has_slow =
+		(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+		(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
+		((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
+		 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))));
+
+	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
+		if (op1_info & (MAY_BE_ANY-MAY_BE_LONG)) {
+			if (op1_info & MAY_BE_DOUBLE) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >4, ZREG_TMP1
+			} else {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9, ZREG_TMP1
+			}
+		}
+		if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_LONG))) {
+			if (op2_info & MAY_BE_DOUBLE) {
+				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3, ZREG_TMP1
+				|.cold_code
+				|3:
+				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
+				}
+				if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+					return 0;
+				}
+				|	b >6
+				|.code
+			} else {
+				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1
+			}
+		}
+		if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr, skip_comparison)) {
+			return 0;
+		}
+		if (op1_info & MAY_BE_DOUBLE) {
+			|.cold_code
+			|4:
+			if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1
+			}
+			if (op2_info & MAY_BE_DOUBLE) {
+				if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
+					if (!same_ops) {
+						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >5, ZREG_TMP1
+					} else {
+						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
+					}
+				}
+				if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+					return 0;
+				}
+				|	b >6
+			}
+			if (!same_ops) {
+				|5:
+				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1
+				}
+				if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+					return 0;
+				}
+				|	b >6
+			}
+			|.code
+		}
+	} else if ((op1_info & MAY_BE_DOUBLE) &&
+	           !(op1_info & MAY_BE_LONG) &&
+	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+		if (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1
+		}
+		if (op2_info & MAY_BE_DOUBLE) {
+			if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
+				if (!same_ops && (op2_info & MAY_BE_LONG)) {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >3, ZREG_TMP1
+				} else {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
+				}
+			}
+			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+				return 0;
+			}
+		}
+		if (!same_ops && (op2_info & MAY_BE_LONG)) {
+			if (op2_info & MAY_BE_DOUBLE) {
+				|.cold_code
+			}
+		    |3:
+			if (op2_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
+				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1
+			}
+			if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+				return 0;
+			}
+			if (op2_info & MAY_BE_DOUBLE) {
+				|	b >6
+				|.code
+			}
+		}
+	} else if ((op2_info & MAY_BE_DOUBLE) &&
+	           !(op2_info & MAY_BE_LONG) &&
+	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+		if (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
+			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
+		}
+		if (op1_info & MAY_BE_DOUBLE) {
+			if (!same_ops && (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
+				if (!same_ops && (op1_info & MAY_BE_LONG)) {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >3, ZREG_TMP1
+				} else {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1
+				}
+			}
+			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+				return 0;
+			}
+		}
+		if (!same_ops && (op1_info & MAY_BE_LONG)) {
+			if (op1_info & MAY_BE_DOUBLE) {
+				|.cold_code
+			}
+			|3:
+			if (op1_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9, ZREG_TMP1
+			}
+			if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+				return 0;
+			}
+			if (op1_info & MAY_BE_DOUBLE) {
+				|	b >6
+				|.code
+			}
+		}
+	}
+
+	if (has_slow ||
+	    (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
+	    (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
+		if (has_slow) {
+			|.cold_code
+			|9:
+		}
+		|	SET_EX_OPLINE opline, REG0
+		if (Z_MODE(op1_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
+			if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
+				return 0;
+			}
+			op1_addr = real_addr;
+		}
+		if (Z_MODE(op2_addr) == IS_REG) {
+			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
+			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
+				return 0;
+			}
+			op2_addr = real_addr;
+		}
+		|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
+		if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) {
+			|	IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w
+			|	LOAD_32BIT_VAL FCARG1x, opline->op1.var
+			|	EXT_CALL zend_jit_undefined_op_helper, REG0
+			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
+			|1:
+		}
+		if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) {
+			|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1
+			|	str FCARG2x, T1 // save
+			|	LOAD_32BIT_VAL FCARG1x, opline->op2.var
+			|	EXT_CALL zend_jit_undefined_op_helper, REG0
+			|	ldr FCARG2x, T1 // restore
+			|	LOAD_ADDR_ZTS CARG3, executor_globals, uninitialized_zval
+			|	b >2
+			|1:
+			|	LOAD_ZVAL_ADDR CARG3, op2_addr
+			|2:
+		} else {
+			|	LOAD_ZVAL_ADDR CARG3, op2_addr
+		}
+		|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+		|	EXT_CALL compare_function, REG0
+		if (opline->opcode != ZEND_CASE) {
+			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		}
+		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		if (may_throw) {
+			zend_jit_check_exception_undef_result(Dst, opline);
+		}
+		if (!zend_jit_cmp_slow(Dst, opline, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+			return 0;
+		}
+		if (has_slow) {
+			|	b >6
+			|.code
+		}
+	}
+
+	|6:
+
+	return 1;
+}
+
+static int zend_jit_identical(dasm_State    **Dst,
+                              const zend_op  *opline,
+                              uint32_t        op1_info,
+                              zend_ssa_range *op1_range,
+                              zend_jit_addr   op1_addr,
+                              uint32_t        op2_info,
+                              zend_ssa_range *op2_range,
+                              zend_jit_addr   op2_addr,
+                              zend_jit_addr   res_addr,
+                              int             may_throw,
+                              zend_uchar      smart_branch_opcode,
+                              uint32_t        target_label,
+                              uint32_t        target_label2,
+                              const void     *exit_addr,
+                              bool            skip_comparison)
+{
+	uint32_t identical_label = (uint32_t)-1;
+	uint32_t not_identical_label = (uint32_t)-1;
+
+	if (smart_branch_opcode && !exit_addr) {
+		if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
+			if (smart_branch_opcode == ZEND_JMPZ) {
+				not_identical_label = target_label;
+			} else if (smart_branch_opcode == ZEND_JMPNZ) {
+				identical_label = target_label;
+			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+				not_identical_label = target_label;
+				identical_label = target_label2;
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		} else {
+			if (smart_branch_opcode == ZEND_JMPZ) {
+				identical_label = target_label;
+			} else if (smart_branch_opcode == ZEND_JMPNZ) {
+				not_identical_label = target_label;
+			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+				identical_label = target_label;
+				not_identical_label = target_label2;
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		}
+	}
+
+	if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
+	    (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
+		if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr, skip_comparison)) {
+			return 0;
+		}
+		return 1;
+	} else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE &&
+	           (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) {
+		if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+			return 0;
+		}
+		return 1;
+	}
+
+	if ((op1_info & MAY_BE_UNDEF) && (op2_info & MAY_BE_UNDEF)) {
+		op1_info |= MAY_BE_NULL;
+		op2_info |= MAY_BE_NULL;
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w
+		|.cold_code
+		|1:
+		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+		|	SET_EX_OPLINE opline, REG0
+		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+		|	EXT_CALL zend_jit_undefined_op_helper, REG0
+		if (may_throw) {
+			zend_jit_check_exception_undef_result(Dst, opline);
+		}
+		|	LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
+		|	b >1
+		|.code
+		|1:
+		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		|	IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w
+		|.cold_code
+		|1:
+		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+		|	SET_EX_OPLINE opline, REG0
+		|	str FCARG1x, T1 // save
+		|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
+		|	EXT_CALL zend_jit_undefined_op_helper, REG0
+		if (may_throw) {
+			zend_jit_check_exception_undef_result(Dst, opline);
+		}
+		|	ldr FCARG1x, T1 // restore
+		|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
+		|	b >1
+		|.code
+		|1:
+	} else if (op1_info & MAY_BE_UNDEF) {
+		op1_info |= MAY_BE_NULL;
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w
+		|.cold_code
+		|1:
+		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+		|	SET_EX_OPLINE opline, REG0
+		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+		|	EXT_CALL zend_jit_undefined_op_helper, REG0
+		if (may_throw) {
+			zend_jit_check_exception_undef_result(Dst, opline);
+		}
+		|	LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
+		|	b >1
+		|.code
+		|1:
+		if (opline->op2_type != IS_CONST) {
+			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		}
+	} else if (op2_info & MAY_BE_UNDEF) {
+		op2_info |= MAY_BE_NULL;
+		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		|	IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w
+		|.cold_code
+		|1:
+		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+		|	SET_EX_OPLINE opline, REG0
+		|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
+		|	EXT_CALL zend_jit_undefined_op_helper, REG0
+		if (may_throw) {
+			zend_jit_check_exception_undef_result(Dst, opline);
+		}
+		|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
+		|	b >1
+		|.code
+		|1:
+		if (opline->op1_type != IS_CONST) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		}
+	} else if ((op1_info & op2_info & MAY_BE_ANY) != 0) {
+		if (opline->op1_type != IS_CONST) {
+			if (Z_MODE(op1_addr) == IS_REG) {
+				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
+				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
+					return 0;
+				}
+				op1_addr = real_addr;
+			}
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		}
+		if (opline->op2_type != IS_CONST) {
+			if (Z_MODE(op2_addr) == IS_REG) {
+				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
+				if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
+					return 0;
+				}
+				op2_addr = real_addr;
+			}
+			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		}
+	}
+
+	if ((op1_info & op2_info & MAY_BE_ANY) == 0) {
+		if ((opline->opcode != ZEND_CASE_STRICT &&
+		     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+		     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
+		    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
+		     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
+			|	SET_EX_OPLINE opline, REG0
+			if (opline->opcode != ZEND_CASE_STRICT) {
+				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+			}
+			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+		}
+		if (smart_branch_opcode) {
+			if (may_throw) {
+				zend_jit_check_exception_undef_result(Dst, opline);
+			}
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPZ) {
+					|	b &exit_addr
+				}
+			} else if (not_identical_label != (uint32_t)-1) {
+				|	b =>not_identical_label
+			}
+		} else {
+			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2
+			if (may_throw) {
+				zend_jit_check_exception(Dst);
+			}
+		}
+		return 1;
+	}
+
+	if (opline->op1_type & (IS_CV|IS_VAR)) {
+		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+	}
+	if (opline->op2_type & (IS_CV|IS_VAR)) {
+		|	ZVAL_DEREF FCARG2x, op2_info, TMP1w
+	}
+
+	if (has_concrete_type(op1_info)
+	 && has_concrete_type(op2_info)
+	 && concrete_type(op1_info) == concrete_type(op2_info)
+	 && concrete_type(op1_info) <= IS_TRUE) {
+		if (smart_branch_opcode) {
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPNZ) {
+					|	b &exit_addr
+				}
+			} else if (identical_label != (uint32_t)-1) {
+				|	b =>identical_label
+			}
+		} else {
+			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2
+		}
+	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+		if (zend_is_identical(Z_ZV(op1_addr), Z_ZV(op2_addr))) {
+			if (smart_branch_opcode) {
+				if (exit_addr) {
+					if (smart_branch_opcode == ZEND_JMPNZ) {
+						|	b &exit_addr
+					}
+				} else if (identical_label != (uint32_t)-1) {
+					|	b =>identical_label
+				}
+			} else {
+				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2
+			}
+		} else {
+			if (smart_branch_opcode) {
+				if (exit_addr) {
+					if (smart_branch_opcode == ZEND_JMPZ) {
+						|	b &exit_addr
+					}
+				} else if (not_identical_label != (uint32_t)-1) {
+					|	b =>not_identical_label
+				}
+			} else {
+				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2
+			}
+		}
+	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op1_addr)) <= IS_TRUE) {
+		zval *val = Z_ZV(op1_addr);
+
+		|	ldrb TMP1w, [FCARG2x, #offsetof(zval, u1.v.type)]
+		|	cmp TMP1w, #Z_TYPE_P(val)
+		if (smart_branch_opcode) {
+			if (opline->op2_type == IS_VAR && (op2_info & MAY_BE_REF)) {
+				|	bne >8
+				|	SET_EX_OPLINE opline, REG0
+				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+				if (may_throw) {
+					zend_jit_check_exception_undef_result(Dst, opline);
+				}
+				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
+					|	b &exit_addr
+				} else if (identical_label != (uint32_t)-1) {
+					|	b =>identical_label
+				} else {
+					|	b >9
+				}
+				|8:
+			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
+				|	beq &exit_addr
+			} else if (identical_label != (uint32_t)-1) {
+				|	beq =>identical_label
+			} else {
+				|	beq >9
+			}
+		} else {
+			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
+				|	cset REG0w, eq
+			} else {
+				|	cset REG0w, ne
+			}
+			|	add REG0w, REG0w, #2
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+		}
+		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
+		    (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+			|	SET_EX_OPLINE opline, REG0
+			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+			if (may_throw) {
+				zend_jit_check_exception_undef_result(Dst, opline);
+			}
+		}
+		if (exit_addr) {
+			if (smart_branch_opcode == ZEND_JMPZ) {
+				|	b &exit_addr
+			}
+		} else if (smart_branch_opcode && not_identical_label != (uint32_t)-1) {
+			|	b =>not_identical_label
+		}
+	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op2_addr)) <= IS_TRUE) {
+		zval *val = Z_ZV(op2_addr);
+
+		|	ldrb TMP1w, [FCARG1x, #offsetof(zval, u1.v.type)]
+		|	cmp TMP1w, #Z_TYPE_P(val)
+		if (smart_branch_opcode) {
+			if (opline->opcode != ZEND_CASE_STRICT
+			 && opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) {
+				|	bne >8
+				|	SET_EX_OPLINE opline, REG0
+				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+				if (may_throw) {
+					zend_jit_check_exception_undef_result(Dst, opline);
+				}
+				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
+					|	b &exit_addr
+				} else if (identical_label != (uint32_t)-1) {
+					|	b =>identical_label
+				} else {
+					|	b >9
+				}
+				|8:
+			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
+				|	beq &exit_addr
+			} else if (identical_label != (uint32_t)-1) {
+				|	beq =>identical_label
+			} else {
+				|	beq >9
+			}
+		} else {
+			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
+				|	cset REG0w, eq
+			} else {
+				|	cset REG0w, ne
+			}
+			|	add REG0w, REG0w, #2
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+		}
+		if (opline->opcode != ZEND_CASE_STRICT
+		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+		    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+			|	SET_EX_OPLINE opline, REG0
+			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+			if (may_throw) {
+				zend_jit_check_exception_undef_result(Dst, opline);
+			}
+		}
+		if (smart_branch_opcode) {
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPZ) {
+					|	b &exit_addr
+				}
+			} else if (not_identical_label != (uint32_t)-1) {
+				|	b =>not_identical_label
+			}
+		}
+	} else {
+		if (opline->op1_type == IS_CONST) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		}
+		if (opline->op2_type == IS_CONST) {
+			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		}
+		|	EXT_CALL zend_is_identical, REG0
+			if ((opline->opcode != ZEND_CASE_STRICT &&
+			     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
+			    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
+			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
+				|	str RETVALw, T1 // save
+				|	SET_EX_OPLINE opline, REG0
+				if (opline->opcode != ZEND_CASE_STRICT) {
+					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+				}
+				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+				if (may_throw) {
+					zend_jit_check_exception_undef_result(Dst, opline);
+				}
+				|	ldr RETVALw, T1 // restore
+			}
+		if (smart_branch_opcode) {
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPNZ) {
+					|	cbnz RETVALw, &exit_addr
+				} else {
+					|	cbz RETVALw, &exit_addr
+				}
+			} else if (not_identical_label != (uint32_t)-1) {
+				|	cbz RETVALw, =>not_identical_label
+				if (identical_label != (uint32_t)-1) {
+					|	b =>identical_label
+				}
+			} else if (identical_label != (uint32_t)-1) {
+				|	cbnz RETVALw, =>identical_label
+			}
+		} else {
+			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
+				|	add RETVALw, RETVALw, #2
+			} else {
+				|	neg RETVALw, RETVALw
+				|	add RETVALw, RETVALw, #3
+			}
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, RETVALw, TMP1
+		}
+	}
+
+	|9:
+	if (may_throw) {
+		zend_jit_check_exception(Dst);
+	}
+	return 1;
+}
+
+static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr res_addr, uint32_t target_label, uint32_t target_label2, int may_throw, zend_uchar branch_opcode, const void *exit_addr)
+{
+	uint32_t true_label = -1;
+	uint32_t false_label = -1;
+	bool set_bool = 0;
+	bool set_bool_not = 0;
+	bool set_delayed = 0;
+	bool jmp_done = 0;
+
+	if (branch_opcode == ZEND_BOOL) {
+		set_bool = 1;
+	} else if (branch_opcode == ZEND_BOOL_NOT) {
+		set_bool = 1;
+		set_bool_not = 1;
+	} else if (branch_opcode == ZEND_JMPZ) {
+		false_label = target_label;
+	} else if (branch_opcode == ZEND_JMPNZ) {
+		true_label = target_label;
+	} else if (branch_opcode == ZEND_JMPZNZ) {
+		true_label = target_label2;
+		false_label = target_label;
+	} else if (branch_opcode == ZEND_JMPZ_EX) {
+		set_bool = 1;
+		false_label = target_label;
+	} else if (branch_opcode == ZEND_JMPNZ_EX) {
+		set_bool = 1;
+		true_label = target_label;
+	} else {
+		ZEND_UNREACHABLE();
+	}
+
+	if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
+		if (zend_is_true(Z_ZV(op1_addr))) {
+			/* Always TRUE */
+			if (set_bool) {
+				if (set_bool_not) {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+				} else {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+				}
+			}
+			if (true_label != (uint32_t)-1) {
+				|	b =>true_label
+			}
+		} else {
+			/* Always FALSE */
+			if (set_bool) {
+				if (set_bool_not) {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+				} else {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+				}
+			}
+			if (false_label != (uint32_t)-1) {
+				|	b =>false_label
+			}
+		}
+		return 1;
+	}
+
+	if (op1_info & MAY_BE_REF) {
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	}
+
+	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE)) {
+		if (!(op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_TRUE))) {
+			/* Always TRUE */
+			if (set_bool) {
+				if (set_bool_not) {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+				} else {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+				}
+			}
+			if (true_label != (uint32_t)-1) {
+				|	b =>true_label
+			}
+		} else {
+			if (!(op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE)))) {
+				/* Always FALSE */
+				if (set_bool) {
+					if (set_bool_not) {
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					} else {
+						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					}
+				}
+			} else {
+				|	CMP_ZVAL_TYPE op1_addr, IS_TRUE, ZREG_TMP1
+				if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
+				    if ((op1_info & MAY_BE_LONG) &&
+				        !(op1_info & MAY_BE_UNDEF) &&
+				        !set_bool) {
+						if (exit_addr) {
+							if (branch_opcode == ZEND_JMPNZ) {
+								|	blt >9
+							} else {
+								|	blt &exit_addr
+							}
+						} else if (false_label != (uint32_t)-1) {
+							|	blt =>false_label
+						} else {
+							|	blt >9
+						}
+						jmp_done = 1;
+					} else {
+						|	bgt >2
+					}
+				}
+				if (!(op1_info & MAY_BE_TRUE)) {
+					/* It's FALSE */
+					if (set_bool) {
+						if (set_bool_not) {
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+						} else {
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+						}
+					}
+				} else {
+					if (exit_addr) {
+						if (set_bool) {
+							|	bne >1
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+								|	b &exit_addr
+							} else {
+								|	b >9
+							}
+							|1:
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+							if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
+								if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
+									|	bne &exit_addr
+								}
+							}
+						} else {
+							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+								|	beq &exit_addr
+							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
+								|	bne &exit_addr
+							} else {
+								|	beq >9
+							}
+						}
+					} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
+						if (set_bool) {
+							|	bne >1
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+							if (true_label != (uint32_t)-1) {
+								|	b =>true_label
+							} else {
+								|	b >9
+							}
+							|1:
+							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+						} else {
+							if (true_label != (uint32_t)-1) {
+								|	beq =>true_label
+							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
+								|	bne =>false_label
+								jmp_done = 1;
+							} else {
+								|	beq >9
+							}
+						}
+					} else if (set_bool) {
+						|	cset REG0w, eq
+						if (set_bool_not) {
+							|	neg REG0w, REG0w
+							|	add REG0w, REG0w, #3
+						} else {
+							|	add REG0w, REG0w, #2
+						}
+						if ((op1_info & MAY_BE_UNDEF) && (op1_info & MAY_BE_ANY)) {
+							set_delayed = 1;
+						} else {
+							|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+						}
+					}
+				}
+			}
+
+			/* It's FALSE, but may be UNDEF */
+			if (op1_info & MAY_BE_UNDEF) {
+				if (op1_info & MAY_BE_ANY) {
+					if (set_delayed) {
+						|	CMP_ZVAL_TYPE op1_addr, IS_UNDEF, ZREG_TMP1
+						|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+						|	beq >1
+					} else {
+						|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+					}
+					|.cold_code
+					|1:
+				}
+				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+				|	SET_EX_OPLINE opline, REG0
+				|	EXT_CALL zend_jit_undefined_op_helper, REG0
+
+				if (may_throw) {
+					if (!zend_jit_check_exception_undef_result(Dst, opline)) {
+						return 0;
+					}
+				}
+
+				if (exit_addr) {
+					if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
+						|	b &exit_addr
+					}
+				} else if (false_label != (uint32_t)-1) {
+					|	b =>false_label
+				}
+				if (op1_info & MAY_BE_ANY) {
+					if (exit_addr) {
+						if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+							|	b >9
+						}
+					} else if (false_label == (uint32_t)-1) {
+						|	b >9
+					}
+					|.code
+				}
+			}
+
+			if (!jmp_done) {
+				if (exit_addr) {
+					if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+						if (op1_info & MAY_BE_LONG) {
+							|	b >9
+						}
+					} else if (op1_info & MAY_BE_LONG) {
+						|	b &exit_addr
+					}
+				} else if (false_label != (uint32_t)-1) {
+					|	b =>false_label
+				} else if (op1_info & MAY_BE_LONG) {
+					|	b >9
+				}
+			}
+		}
+	}
+
+	if (op1_info & MAY_BE_LONG) {
+		|2:
+		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2, ZREG_TMP1
+		}
+		if (Z_MODE(op1_addr) == IS_REG) {
+			|	tst Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr))
+		} else {
+			|	LONG_CMP_WITH_CONST op1_addr, Z_L(0), TMP1, TMP2
+		}
+		if (set_bool) {
+			|	cset REG0w, ne
+			if (set_bool_not) {
+				|	neg REG0w, REG0w
+				|	add REG0w, REG0w, #3
+			} else {
+				|	add REG0w, REG0w, #2
+			}
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+		}
+		if (exit_addr) {
+			if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+				|	bne &exit_addr
+			} else {
+				|	beq &exit_addr
+			}
+		} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
+			if (true_label != (uint32_t)-1) {
+				|	bne =>true_label
+				if (false_label != (uint32_t)-1) {
+					|	b =>false_label
+				}
+			} else {
+				|	beq =>false_label
+			}
+		}
+	}
+
+	if ((op1_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+		|	mov TMP1, xzr
+		|	fmov FPR0d, TMP1
+		|	DOUBLE_CMP ZREG_FPR0, op1_addr, ZREG_TMP1, ZREG_FPTMP
+
+		if (set_bool) {
+			if (exit_addr) {
+				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+					|	bvs >1
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+					|	bne &exit_addr
+					|1:
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+				} else {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+					|	bvs &exit_addr
+					|	beq &exit_addr
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+				}
+			} else if (false_label != (uint32_t)-1) { // JMPZ_EX
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+				|	bvs >1
+				|	beq => false_label
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+				|1:
+			} else if (true_label != (uint32_t)-1) { // JMPNZ_EX
+				|	bvs >1
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+				|	bne => true_label
+				|1:
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+			} else if (set_bool_not) { // BOOL_NOT
+				|	bvs >1
+				|	mov REG0w, #IS_TRUE
+				|	beq >2
+				|1:
+				|	mov REG0w, #IS_FALSE
+				|2:
+				|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+			} else { // BOOL
+				|	bvs >1
+				|	mov REG0w, #IS_TRUE
+				|	bne >2
+				|1:
+				|	mov REG0w, #IS_FALSE
+				|2:
+				|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+			}
+		} else {
+			if (exit_addr) {
+				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+					|	bvs >1
+					|	bne &exit_addr
+					|1:
+				} else {
+					|	bvs &exit_addr
+					|	beq &exit_addr
+				}
+			} else {
+				ZEND_ASSERT(true_label != (uint32_t)-1 || false_label != (uint32_t)-1);
+				if (false_label != (uint32_t)-1) {
+					|	bvs =>false_label
+				} else {
+					|	bvs >1
+				}
+				if (true_label != (uint32_t)-1) {
+					|	bne =>true_label
+					if (false_label != (uint32_t)-1) {
+						|	b =>false_label
+					}
+				} else {
+					|	beq =>false_label
+				}
+				|1:
+			}
+		}
+	} else if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
+		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+			|.cold_code
+			|2:
+		}
+		if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		}
+		|	SET_EX_OPLINE opline, REG0
+		|	EXT_CALL zend_is_true, REG0
+		|	mov REG0, RETVALx
+
+		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+			(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
+
+			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+				|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >3, ZREG_TMP1, ZREG_TMP2
+			}
+			|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+			|	GC_DELREF FCARG1x, TMP1w
+			|	bne >3
+			// In x86, r0 is used in macro ZVAL_DTOR_FUNC as temporary register, hence, r0 should be saved/restored
+			// before/after this macro. In AArch64, TMP1 is used, but we still have to store REG0,
+			// because it's clobbered by function call.
+			|	str REG0, T1 // save
+			|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
+			|	ldr REG0, T1 // restore
+			|3:
+		}
+		if (may_throw) {
+			|	MEM_LOAD_ZTS ldr, REG1, executor_globals, exception, TMP1
+			|	cbnz REG1, ->exception_handler_undef
+		}
+
+		if (set_bool) {
+			if (set_bool_not) {
+				|	neg REG0w, REG0w
+				|	add REG0w, REG0w, #3
+			} else {
+				|	add REG0w, REG0w, #2
+			}
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+			if (exit_addr) {
+				|	CMP_ZVAL_TYPE res_addr, IS_FALSE, ZREG_TMP1
+				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+					|	bne &exit_addr
+				} else {
+					|	beq &exit_addr
+				}
+			} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
+				|	CMP_ZVAL_TYPE res_addr, IS_FALSE, ZREG_TMP1
+				if (true_label != (uint32_t)-1) {
+					|	bne =>true_label
+					if (false_label != (uint32_t)-1) {
+						|	b =>false_label
+					} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+						|	b >9
+					}
+				} else {
+					|	beq =>false_label
+				}
+			}
+			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+				|	b >9
+				|.code
+			}
+		} else {
+			if (exit_addr) {
+				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
+					|	cbnz REG0w, &exit_addr
+					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+						|	b >9
+					}
+				} else {
+					|	cbz REG0w, &exit_addr
+					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+						|	b >9
+					}
+				}
+			} else if (true_label != (uint32_t)-1) {
+				|	cbnz REG0w, =>true_label
+				if (false_label != (uint32_t)-1) {
+					|	b =>false_label
+				} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+					|	b >9
+				}
+			} else {
+				|	cbz REG0w, =>false_label
+				if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+					|	b >9
+				}
+			}
+
+			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
+				|.code
+			}
+		}
+	}
+
+	|9:
+
+	return 1;
+}
+
+static int zend_jit_qm_assign(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr op1_def_addr, uint32_t res_use_info, uint32_t res_info, zend_jit_addr res_addr)
+{
+	if (op1_addr != op1_def_addr) {
+		if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
+			return 0;
+		}
+		if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
+			op1_addr = op1_def_addr;
+		}
+	}
+
+	if (!zend_jit_simple_assign(Dst, opline, res_addr, res_use_info, res_info, opline->op1_type, op1_addr, op1_info, 0, 0, 0)) {
+		return 0;
+	}
+	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
+		return 0;
+	}
+	return 1;
+}
+
+static int zend_jit_assign(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_use_addr, uint32_t op1_def_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, zend_jit_addr op2_def_addr, uint32_t res_info, zend_jit_addr res_addr, int may_throw)
+{
+	ZEND_ASSERT(opline->op1_type == IS_CV);
+
+	if (op2_addr != op2_def_addr) {
+		if (!zend_jit_update_regs(Dst, opline->op2.var, op2_addr, op2_def_addr, op2_info)) {
+			return 0;
+		}
+		if (Z_MODE(op2_def_addr) == IS_REG && Z_MODE(op2_addr) != IS_REG) {
+			op2_addr = op2_def_addr;
+		}
+	}
+
+	if (Z_MODE(op1_addr) != IS_REG
+	 && Z_MODE(op1_use_addr) == IS_REG
+	 && !Z_LOAD(op1_use_addr)
+	 && !Z_STORE(op1_use_addr)) {
+		/* Force type update */
+		op1_info |= MAY_BE_UNDEF;
+	}
+	if (!zend_jit_assign_to_variable(Dst, opline, op1_use_addr, op1_addr, op1_info, op1_def_info, opline->op2_type, op2_addr, op2_info, res_addr,
+			may_throw)) {
+		return 0;
+	}
+	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_addr, op1_def_info, op1_use_addr, op1_info)) {
+		return 0;
+	}
+	if (opline->result_type != IS_UNUSED) {
+		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/* copy of hidden zend_closure */
+typedef struct _zend_closure {
+	zend_object       std;
+	zend_function     func;
+	zval              this_ptr;
+	zend_class_entry *called_scope;
+	zif_handler       orig_internal_handler;
+} zend_closure;
+
+static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_t used_stack)
+{
+	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+	if (!exit_addr) {
+		return 0;
+	}
+
+	|	// Check Stack Overflow
+	|	MEM_LOAD_ZTS ldr, REG1, executor_globals, vm_stack_end, TMP1
+	|	MEM_LOAD_OP_ZTS sub, ldr, REG1, executor_globals, vm_stack_top, TMP1, TMP2
+	|	CMP_64_WITH_CONST_32 REG1, used_stack, TMP1
+	|	blo &exit_addr
+
+	return 1;
+}
+
+static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_function *func, bool is_closure, bool use_this, bool stack_check)
+{
+	uint32_t used_stack;
+
+	// REG0   -> zend_function
+	// FCARG1 -> used_stack
+
+	if (func) {
+		used_stack = zend_vm_calc_used_stack(opline->extended_value, func);
+	} else {
+		used_stack = (ZEND_CALL_FRAME_SLOT + opline->extended_value) * sizeof(zval);
+
+		|	// if (EXPECTED(ZEND_USER_CODE(func->type))) {
+		if (!is_closure) {
+			|	LOAD_32BIT_VAL FCARG1w, used_stack
+			|	// Check whether REG0 is an internal function.
+			|	ldrb TMP1w, [REG0, #offsetof(zend_function, type)]
+			|	TST_32_WITH_CONST TMP1w, 1, TMP2w
+			|	bne >1
+		} else {
+			|	LOAD_32BIT_VAL FCARG1w, used_stack
+		}
+		|	// used_stack += (func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args)) * sizeof(zval);
+		|	LOAD_32BIT_VAL REG2w, opline->extended_value
+		if (!is_closure) {
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, op_array.num_args)]
+			|	cmp REG2w, TMP1w
+			|	csel REG2w, REG2w, TMP1w, le
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, op_array.last_var)]
+			|	sub REG2w, REG2w, TMP1w
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, op_array.T)]
+			|	sub REG2w, REG2w, TMP1w
+		} else {
+			|	ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.num_args)]
+			|	cmp REG2w, TMP1w
+			|	csel REG2w, REG2w, TMP1w, le
+			|	ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.last_var)]
+			|	sub REG2w, REG2w, TMP1w
+			|	ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.T)]
+			|	sub REG2w, REG2w, TMP1w
+		}
+		|	sxtw REG2, REG2w
+		|	sub FCARG1x, FCARG1x, REG2, lsl #4
+		|1:
+	}
+
+	zend_jit_start_reuse_ip();
+
+	|	// if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
+	|	MEM_LOAD_ZTS ldr, RX, executor_globals, vm_stack_top, TMP1
+
+	if (stack_check) {
+		|	// Check Stack Overflow
+		|	MEM_LOAD_ZTS ldr, REG2, executor_globals, vm_stack_end, TMP1
+		|	sub REG2, REG2, RX
+		if (func) {
+			|	CMP_64_WITH_CONST_32 REG2, used_stack, TMP1
+		} else {
+			|	cmp REG2, FCARG1x
+		}
+
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+			if (!exit_addr) {
+				return 0;
+			}
+
+			|	blo &exit_addr
+		} else {
+			|	blo >1
+			|	// EG(vm_stack_top) = (zval*)((char*)call + used_stack);
+			|.cold_code
+			|1:
+			if (func) {
+				|	LOAD_32BIT_VAL FCARG1w, used_stack
+			}
+			if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
+				|	SET_EX_OPLINE opline, REG0
+				|	EXT_CALL zend_jit_int_extend_stack_helper, REG0
+			} else {
+				if (!is_closure) {
+					|	mov FCARG2x, REG0
+				} else {
+					|	add FCARG2x, REG0, #offsetof(zend_closure, func)
+				}
+				|	SET_EX_OPLINE opline, REG0
+				|	EXT_CALL zend_jit_extend_stack_helper, REG0
+			}
+			|	mov RX, RETVALx
+			|	b >1
+			|.code
+		}
+	}
+
+	if (func) {
+		||	if (used_stack <= ADD_SUB_IMM) {
+		|		MEM_LOAD_OP_STORE_ZTS add, ldr, str, #used_stack, executor_globals, vm_stack_top, REG2, TMP1
+		||	} else {
+		|		LOAD_32BIT_VAL TMP1w, used_stack
+		|		MEM_LOAD_OP_STORE_ZTS add, ldr, str, TMP1, executor_globals, vm_stack_top, REG2, TMP2
+		||	}
+	} else {
+		|	MEM_LOAD_OP_STORE_ZTS add, ldr, str, FCARG1x, executor_globals, vm_stack_top, REG2, TMP1
+	}
+	|	// zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
+	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) {
+		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
+		|	LOAD_32BIT_VAL TMP1w, (IS_UNDEF | ZEND_CALL_NESTED_FUNCTION)
+		|	str TMP1w, EX:RX->This.u1.type_info
+	}
+	if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
+		|	// call->func = func;
+		|1:
+		|	ADDR_STORE EX:RX->func, func, REG1
+	} else {
+		if (!is_closure) {
+			|	// call->func = func;
+			if (func
+			 && op_array == &func->op_array
+			 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
+			 && (sizeof(void*) != 8 || IS_SIGNED_32BIT(func))) {
+				|	LOAD_ADDR TMP1, func
+				|	str TMP1, EX:RX->func
+			} else {
+				|	str REG0, EX:RX->func
+			}
+		} else {
+			|	// call->func = &closure->func;
+			|	add REG1, REG0, #offsetof(zend_closure, func)
+			|	str REG1, EX:RX->func
+		}
+		|1:
+	}
+	if (opline->opcode == ZEND_INIT_METHOD_CALL) {
+		|	// Z_PTR(call->This) = obj;
+		|	ldr REG1, T1
+		|	str REG1, EX:RX->This.value.ptr
+	    if (opline->op1_type == IS_UNUSED || use_this) {
+			|	// call->call_info |= ZEND_CALL_HAS_THIS;
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				|	LOAD_32BIT_VAL TMP1w, ZEND_CALL_HAS_THIS
+				|	str TMP1w, EX:RX->This.u1.type_info
+			} else {
+				|	LOAD_32BIT_VAL TMP1w, ZEND_CALL_HAS_THIS
+				|	ldr TMP2w, EX:RX->This.u1.type_info
+				|	orr TMP2w, TMP2w, TMP1w
+				|	str TMP2w, EX:RX->This.u1.type_info
+			}
+	    } else {
+			if (opline->op1_type == IS_CV) {
+				|	// GC_ADDREF(obj);
+				|	GC_ADDREF REG1, TMP1w
+			}
+			|	// call->call_info |= ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS;
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				|	LOAD_32BIT_VAL TMP1w, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS)
+				|	str TMP1w, EX:RX->This.u1.type_info
+			} else {
+				|	ldr TMP1w, EX:RX->This.u1.type_info
+				|	BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS), TMP2w
+				|	str TMP1w, EX:RX->This.u1.type_info
+			}
+	    }
+	} else if (!is_closure) {
+		|	// Z_CE(call->This) = called_scope;
+		|	str xzr, EX:RX->This.value.ptr
+	} else {
+		if (opline->op2_type == IS_CV) {
+			|	// GC_ADDREF(closure);
+			|	ldr TMP1w, [REG0]
+			|	add TMP1w, TMP1w, #1
+			|	str TMP1w, [REG0]
+		}
+		|	//	object_or_called_scope = closure->called_scope;
+		|	ldr REG1, [REG0, #offsetof(zend_closure, called_scope)]
+		|	str REG1, EX:RX->This.value.ptr
+		|	// call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE |
+		|	//	(closure->func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE);
+		|	ldr REG2w, [REG0, #offsetof(zend_closure, func.common.fn_flags)]
+		|	BW_OP_32_WITH_CONST and, REG2w, REG2w, ZEND_ACC_FAKE_CLOSURE, TMP1w
+		|	BW_OP_32_WITH_CONST orr, REG2w, REG2w, (ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE), TMP1w
+		|	//	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
+		|	ldrb TMP1w, [REG0, #offsetof(zend_closure, this_ptr.u1.v.type)]
+		|	cmp TMP1w, #IS_UNDEF
+		|	beq >1
+		|	//	call_info |= ZEND_CALL_HAS_THIS;
+		|	BW_OP_32_WITH_CONST orr, REG2w, REG2w, ZEND_CALL_HAS_THIS, TMP1w
+		|	//	object_or_called_scope = Z_OBJ(closure->this_ptr);
+		|	ldr REG1, [REG0, #offsetof(zend_closure, this_ptr.value.ptr)]
+	    |1:
+		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
+		|	ldr TMP1w, EX:RX->This.u1.type_info
+		|	orr TMP1w, TMP1w, REG2w
+		|	str TMP1w, EX:RX->This.u1.type_info
+		|	// Z_PTR(call->This) = object_or_called_scope;
+		|	str REG1, EX:RX->This.value.ptr
+		|	ldr TMP1, [REG0, #offsetof(zend_closure, func.op_array.run_time_cache__ptr)]
+		|	cbnz TMP1, >1
+		|	add FCARG1x, REG0, #offsetof(zend_closure, func)
+		|	EXT_CALL zend_jit_init_func_run_time_cache_helper, REG0
+		|1:
+	}
+	|	// ZEND_CALL_NUM_ARGS(call) = num_args;
+	|	LOAD_32BIT_VAL TMP1w, opline->extended_value
+	|	str TMP1w, EX:RX->This.u2.num_args
+
+	return 1;
+}
+
+static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, const zend_op *opline, zend_jit_trace_rec *trace)
+{
+	int skip;
+
+	if (trace) {
+		zend_jit_trace_rec *p = trace;
+
+		ssa_op++;
+		while (1) {
+			if (p->op == ZEND_JIT_TRACE_VM) {
+				switch (p->opline->opcode) {
+					case ZEND_SEND_ARRAY:
+					case ZEND_SEND_USER:
+					case ZEND_SEND_UNPACK:
+					case ZEND_INIT_FCALL:
+					case ZEND_INIT_METHOD_CALL:
+					case ZEND_INIT_STATIC_METHOD_CALL:
+					case ZEND_INIT_FCALL_BY_NAME:
+					case ZEND_INIT_NS_FCALL_BY_NAME:
+					case ZEND_INIT_DYNAMIC_CALL:
+					case ZEND_NEW:
+					case ZEND_INIT_USER_CALL:
+					case ZEND_FAST_CALL:
+					case ZEND_JMP:
+					case ZEND_JMPZNZ:
+					case ZEND_JMPZ:
+					case ZEND_JMPNZ:
+					case ZEND_JMPZ_EX:
+					case ZEND_JMPNZ_EX:
+					case ZEND_FE_RESET_R:
+					case ZEND_FE_RESET_RW:
+					case ZEND_JMP_SET:
+					case ZEND_COALESCE:
+					case ZEND_JMP_NULL:
+					case ZEND_ASSERT_CHECK:
+					case ZEND_CATCH:
+					case ZEND_DECLARE_ANON_CLASS:
+					case ZEND_FE_FETCH_R:
+					case ZEND_FE_FETCH_RW:
+						return 1;
+					case ZEND_DO_ICALL:
+					case ZEND_DO_UCALL:
+					case ZEND_DO_FCALL_BY_NAME:
+					case ZEND_DO_FCALL:
+						return 0;
+					case ZEND_SEND_VAL:
+					case ZEND_SEND_VAR:
+					case ZEND_SEND_VAL_EX:
+					case ZEND_SEND_VAR_EX:
+					case ZEND_SEND_FUNC_ARG:
+					case ZEND_SEND_REF:
+					case ZEND_SEND_VAR_NO_REF:
+					case ZEND_SEND_VAR_NO_REF_EX:
+						/* skip */
+						break;
+					default:
+						if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
+							return 1;
+						}
+				}
+				ssa_op += zend_jit_trace_op_len(opline);
+			} else if (p->op == ZEND_JIT_TRACE_ENTER ||
+			           p->op == ZEND_JIT_TRACE_BACK ||
+			           p->op == ZEND_JIT_TRACE_END) {
+				return 1;
+			}
+			p++;
+		}
+	}
+
+	if (!call_info) {
+		const zend_op *end = op_array->opcodes + op_array->last;
+
+		opline++;
+		ssa_op++;
+		skip = 1;
+		while (opline != end) {
+			if (!skip) {
+				if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
+					return 1;
+				}
+			}
+			switch (opline->opcode) {
+				case ZEND_SEND_VAL:
+				case ZEND_SEND_VAR:
+				case ZEND_SEND_VAL_EX:
+				case ZEND_SEND_VAR_EX:
+				case ZEND_SEND_FUNC_ARG:
+				case ZEND_SEND_REF:
+				case ZEND_SEND_VAR_NO_REF:
+				case ZEND_SEND_VAR_NO_REF_EX:
+					skip = 0;
+					break;
+				case ZEND_SEND_ARRAY:
+				case ZEND_SEND_USER:
+				case ZEND_SEND_UNPACK:
+				case ZEND_INIT_FCALL:
+				case ZEND_INIT_METHOD_CALL:
+				case ZEND_INIT_STATIC_METHOD_CALL:
+				case ZEND_INIT_FCALL_BY_NAME:
+				case ZEND_INIT_NS_FCALL_BY_NAME:
+				case ZEND_INIT_DYNAMIC_CALL:
+				case ZEND_NEW:
+				case ZEND_INIT_USER_CALL:
+				case ZEND_FAST_CALL:
+				case ZEND_JMP:
+				case ZEND_JMPZNZ:
+				case ZEND_JMPZ:
+				case ZEND_JMPNZ:
+				case ZEND_JMPZ_EX:
+				case ZEND_JMPNZ_EX:
+				case ZEND_FE_RESET_R:
+				case ZEND_FE_RESET_RW:
+				case ZEND_JMP_SET:
+				case ZEND_COALESCE:
+				case ZEND_JMP_NULL:
+				case ZEND_ASSERT_CHECK:
+				case ZEND_CATCH:
+				case ZEND_DECLARE_ANON_CLASS:
+				case ZEND_FE_FETCH_R:
+				case ZEND_FE_FETCH_RW:
+					return 1;
+				case ZEND_DO_ICALL:
+				case ZEND_DO_UCALL:
+				case ZEND_DO_FCALL_BY_NAME:
+				case ZEND_DO_FCALL:
+					end = opline;
+					if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
+						/* INIT_FCALL and DO_FCALL in different BasicBlocks */
+						return 1;
+					}
+					return 0;
+			}
+			opline++;
+			ssa_op++;
+		}
+
+		return 1;
+	} else {
+		const zend_op *end = call_info->caller_call_opline;
+
+		if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
+			/* INIT_FCALL and DO_FCALL in different BasicBlocks */
+			return 1;
+		}
+
+		opline++;
+		ssa_op++;
+		skip = 1;
+		while (opline != end) {
+			if (skip) {
+				switch (opline->opcode) {
+					case ZEND_SEND_VAL:
+					case ZEND_SEND_VAR:
+					case ZEND_SEND_VAL_EX:
+					case ZEND_SEND_VAR_EX:
+					case ZEND_SEND_FUNC_ARG:
+					case ZEND_SEND_REF:
+					case ZEND_SEND_VAR_NO_REF:
+					case ZEND_SEND_VAR_NO_REF_EX:
+						skip = 0;
+						break;
+					case ZEND_SEND_ARRAY:
+					case ZEND_SEND_USER:
+					case ZEND_SEND_UNPACK:
+						return 1;
+				}
+			} else {
+				if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
+					return 1;
+				}
+			}
+			opline++;
+			ssa_op++;
+		}
+
+		return 0;
+	}
+}
+
+static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zend_function *func, const zend_op *to_opline)
+{
+	int32_t exit_point;
+	const void *exit_addr;
+
+	if (func->type == ZEND_INTERNAL_FUNCTION) {
+#ifdef ZEND_WIN32
+		// TODO: ASLR may cause different addresses in different workers ???
+		return 0;
+#endif
+	} else if (func->type == ZEND_USER_FUNCTION) {
+		if (!zend_accel_in_shm(func->op_array.opcodes)) {
+			/* op_array and op_array->opcodes are not persistent. We can't link. */
+			return 0;
+		}
+	} else {
+		ZEND_UNREACHABLE();
+		return 0;
+	}
+
+	exit_point = zend_jit_trace_get_exit_point(to_opline, ZEND_JIT_EXIT_POLYMORPHISM);
+	exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+	if (!exit_addr) {
+		return 0;
+	}
+
+	|	// call = EX(call);
+	|	ldr REG1, EX->call
+	while (level > 0) {
+		|	ldr REG1, EX:REG1->prev_execute_data
+		level--;
+	}
+
+	if (func->type == ZEND_USER_FUNCTION &&
+	    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
+	     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
+	     !func->common.function_name)) {
+		const zend_op *opcodes = func->op_array.opcodes;
+
+		|	ldr REG1, EX:REG1->func
+		|	LOAD_ADDR REG2, ((ptrdiff_t)opcodes)
+		|	ldr TMP1, [REG1, #offsetof(zend_op_array, opcodes)]
+		|	cmp TMP1, REG2
+		|	bne &exit_addr
+	} else {
+		|	LOAD_ADDR REG2, ((ptrdiff_t)func)
+		|	ldr TMP1, EX:REG1->func
+		|	cmp TMP1, REG2
+		|	bne &exit_addr
+	}
+
+	return 1;
+}
+
+static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace, bool stack_check)
+{
+	zend_func_info *info = ZEND_FUNC_INFO(op_array);
+	zend_call_info *call_info = NULL;
+	zend_function *func = NULL;
+
+	if (delayed_call_chain) {
+		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
+			return 0;
+		}
+	}
+
+	if (info) {
+		call_info = info->callee_info;
+		while (call_info && call_info->caller_init_opline != opline) {
+			call_info = call_info->next_callee;
+		}
+		if (call_info && call_info->callee_func && !call_info->is_prototype) {
+			func = call_info->callee_func;
+		}
+	}
+
+	if (!func
+	 && trace
+	 && trace->op == ZEND_JIT_TRACE_INIT_CALL) {
+		func = (zend_function*)trace->func;
+	}
+
+	if (opline->opcode == ZEND_INIT_FCALL
+	 && func
+	 && func->type == ZEND_INTERNAL_FUNCTION) {
+		/* load constant address later */
+	} else if (func && op_array == &func->op_array) {
+		/* recursive call */
+		|	ldr REG0, EX->func
+	} else {
+		|	// if (CACHED_PTR(opline->result.num))
+		|	ldr REG0, EX->run_time_cache
+		|	ldr REG0, [REG0, #opline->result.num]
+		|	cbz REG0, >1
+		|.cold_code
+		|1:
+		if (opline->opcode == ZEND_INIT_FCALL
+		 && func
+		 && func->type == ZEND_USER_FUNCTION
+		 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) {
+			|	LOAD_ADDR FCARG1x, func
+			|	EXT_CALL zend_jit_init_func_run_time_cache_helper, REG0
+			|	ldr REG1, EX->run_time_cache
+			|	mov REG0, RETVALx
+			||	ZEND_ASSERT(opline->result.num <= LDR_STR_PIMM64);
+			|	str REG0, [REG1, #opline->result.num]
+			|	b >3
+		} else {
+			zval *zv = RT_CONSTANT(opline, opline->op2);
+
+			if (opline->opcode == ZEND_INIT_FCALL) {
+				|	LOAD_ADDR FCARG1x, Z_STR_P(zv);
+				|	EXT_CALL zend_jit_find_func_helper, REG0
+			} else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) {
+				|	LOAD_ADDR FCARG1x, Z_STR_P(zv + 1);
+				|	EXT_CALL zend_jit_find_func_helper, REG0
+			} else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
+				|	LOAD_ADDR FCARG1x, zv;
+				|	EXT_CALL zend_jit_find_ns_func_helper, REG0
+			} else {
+				ZEND_UNREACHABLE();
+			}
+			|	// CACHE_PTR(opline->result.num, fbc);
+			|	ldr REG1, EX->run_time_cache
+			|	// Get the return value of function zend_jit_find_func_helper/zend_jit_find_ns_func_helper
+			|	mov REG0, RETVALx
+			|	str REG0, [REG1, #opline->result.num]
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+
+				if (!func || opline->opcode == ZEND_INIT_FCALL) {
+					|	cbnz REG0, >3
+				} else if (func->type == ZEND_USER_FUNCTION
+					 && !(func->common.fn_flags & ZEND_ACC_IMMUTABLE)) {
+					const zend_op *opcodes = func->op_array.opcodes;
+
+					|	LOAD_ADDR REG1, ((ptrdiff_t)opcodes)
+					|	ldr TMP1, [REG0, #offsetof(zend_op_array, opcodes)]
+					|	cmp TMP1, REG1
+					|	beq >3
+				} else {
+					|	LOAD_ADDR REG1, ((ptrdiff_t)func)
+					|	cmp REG0, REG1
+					|	beq >3
+				}
+				|	b &exit_addr
+			} else {
+				|	cbnz REG0, >3
+				|	// SAVE_OPLINE();
+				|	SET_EX_OPLINE opline, REG0
+				|	b ->undefined_function
+			}
+		}
+		|.code
+		|3:
+	}
+
+	if (!zend_jit_push_call_frame(Dst, opline, op_array, func, 0, 0, stack_check)) {
+		return 0;
+	}
+
+	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, trace)) {
+		if (!zend_jit_save_call_chain(Dst, call_level)) {
+			return 0;
+		}
+	} else {
+		delayed_call_chain = 1;
+		delayed_call_level = call_level;
+	}
+
+	return 1;
+}
+
+static int zend_jit_init_method_call(dasm_State          **Dst,
+                                     const zend_op        *opline,
+                                     uint32_t              b,
+                                     const zend_op_array  *op_array,
+                                     zend_ssa             *ssa,
+                                     const zend_ssa_op    *ssa_op,
+                                     int                   call_level,
+                                     uint32_t              op1_info,
+                                     zend_jit_addr         op1_addr,
+                                     zend_class_entry     *ce,
+                                     bool                  ce_is_instanceof,
+                                     bool                  use_this,
+                                     zend_class_entry     *trace_ce,
+                                     zend_jit_trace_rec   *trace,
+                                     bool                  stack_check,
+                                     bool                  polymorphic_side_trace)
+{
+	zend_func_info *info = ZEND_FUNC_INFO(op_array);
+	zend_call_info *call_info = NULL;
+	zend_function *func = NULL;
+	zval *function_name;
+
+	ZEND_ASSERT(opline->op2_type == IS_CONST);
+	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
+
+	function_name = RT_CONSTANT(opline, opline->op2);
+
+	if (info) {
+		call_info = info->callee_info;
+		while (call_info && call_info->caller_init_opline != opline) {
+			call_info = call_info->next_callee;
+		}
+		if (call_info && call_info->callee_func && !call_info->is_prototype) {
+			func = call_info->callee_func;
+		}
+	}
+
+	if (polymorphic_side_trace) {
+		/* function is passed in r0 from parent_trace */
+	} else {
+		if (opline->op1_type == IS_UNUSED || use_this) {
+			zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+
+			|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
+		} else {
+		    if (op1_info & MAY_BE_REF) {
+				if (opline->op1_type == IS_CV) {
+					if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+						|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+					}
+					|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+				} else {
+					/* Hack: Convert reference to regular value to simplify JIT code */
+					ZEND_ASSERT(Z_REG(op1_addr) == ZREG_FP);
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1
+					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+					|	EXT_CALL zend_jit_unref_helper, REG0
+					|1:
+				}
+			}
+			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
+				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+					int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+					const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+					if (!exit_addr) {
+						return 0;
+					}
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
+				} else {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
+					|.cold_code
+					|1:
+					if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+						|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+					}
+					|	SET_EX_OPLINE opline, REG0
+					if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
+						|	EXT_CALL zend_jit_invalid_method_call_tmp, REG0
+					} else {
+						|	EXT_CALL zend_jit_invalid_method_call, REG0
+					}
+					|	b ->exception_handler
+					|.code
+				}
+			}
+			|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+		}
+
+		if (delayed_call_chain) {
+			if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
+				return 0;
+			}
+		}
+
+		|	str FCARG1x, T1 // save
+
+		if (func) {
+			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
+			|	ldr REG0, EX->run_time_cache
+			|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, (opline->result.num + sizeof(void*)), TMP1
+			|	cbz REG0, >1
+		} else {
+			|	// if (CACHED_PTR(opline->result.num) == obj->ce)) {
+			|	ldr REG0, EX->run_time_cache
+			|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG2, REG0, opline->result.num, TMP1
+			|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
+			|	cmp REG2, TMP1
+			|	bne >1
+			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
+			|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, (opline->result.num + sizeof(void*)), TMP1
+		}
+
+		|.cold_code
+		|1:
+		|	LOAD_ADDR FCARG2x, function_name
+		if (TMP_ZVAL_OFFSET == 0) {
+			|	mov CARG3, sp
+		} else {
+			|	add CARG3, sp, #TMP_ZVAL_OFFSET
+		}
+		|	SET_EX_OPLINE opline, REG0
+		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
+			|	EXT_CALL zend_jit_find_method_tmp_helper, REG0
+		} else {
+			|	EXT_CALL zend_jit_find_method_helper, REG0
+		}
+		|	mov REG0, RETVALx
+		|	cbnz REG0, >2
+		|	b ->exception_handler
+		|.code
+		|2:
+	}
+
+	if (!func
+	 && trace
+	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
+	 && trace->func
+	) {
+		int32_t exit_point;
+		const void *exit_addr;
+
+		exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_METHOD_CALL);
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+		if (!exit_addr) {
+			return 0;
+		}
+
+		func = (zend_function*)trace->func;
+
+		if (func->type == ZEND_USER_FUNCTION &&
+		    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
+		     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
+		     !func->common.function_name)) {
+			const zend_op *opcodes = func->op_array.opcodes;
+
+			|   LOAD_ADDR TMP1, opcodes
+			|	ldr TMP2, [REG0, #offsetof(zend_op_array, opcodes)]
+			|	cmp TMP2, TMP1
+			|	bne &exit_addr
+		} else {
+			|   LOAD_ADDR TMP1, func
+			|	cmp REG0, TMP1
+			|	bne &exit_addr
+		}
+	}
+
+	if (!func) {
+		|	// if (fbc->common.fn_flags & ZEND_ACC_STATIC) {
+		|	ldr TMP1w, [REG0, #offsetof(zend_function, common.fn_flags)]
+		|	TST_32_WITH_CONST TMP1w, ZEND_ACC_STATIC, TMP2w
+		|	bne >1
+		|.cold_code
+		|1:
+	}
+
+	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) != 0) {
+		|	ldr FCARG1x, T1 // restore
+		|	mov FCARG2x, REG0
+		|	LOAD_32BIT_VAL CARG3w, opline->extended_value
+		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
+			|	EXT_CALL zend_jit_push_static_metod_call_frame_tmp, REG0
+		} else {
+			|	EXT_CALL zend_jit_push_static_metod_call_frame, REG0
+		}
+		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR) && !use_this)) {
+			|	cbz RETVALx, ->exception_handler
+		}
+		|	mov RX, RETVALx
+	}
+
+	if (!func) {
+		|	b >9
+		|.code
+	}
+
+	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) == 0) {
+		if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 0, use_this, stack_check)) {
+			return 0;
+		}
+	}
+
+	if (!func) {
+		|9:
+	}
+	zend_jit_start_reuse_ip();
+
+	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, trace)) {
+		if (!zend_jit_save_call_chain(Dst, call_level)) {
+			return 0;
+		}
+	} else {
+		delayed_call_chain = 1;
+		delayed_call_level = call_level;
+	}
+
+	return 1;
+}
+
+static int zend_jit_init_closure_call(dasm_State          **Dst,
+                                      const zend_op        *opline,
+                                      uint32_t              b,
+                                      const zend_op_array  *op_array,
+                                      zend_ssa             *ssa,
+                                      const zend_ssa_op    *ssa_op,
+                                      int                   call_level,
+                                      zend_jit_trace_rec   *trace,
+                                      bool                  stack_check)
+{
+	zend_function *func = NULL;
+	zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
+
+	|	GET_ZVAL_PTR REG0, op2_addr, TMP1
+
+	if (ssa->var_info[ssa_op->op2_use].ce != zend_ce_closure
+	 && !(ssa->var_info[ssa_op->op2_use].type & MAY_BE_CLASS_GUARD)) {
+		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+		if (!exit_addr) {
+			return 0;
+		}
+
+		|	LOAD_ADDR FCARG1x, ((ptrdiff_t)zend_ce_closure)
+		|	ldr, TMP1, [REG0, #offsetof(zend_object, ce)]
+		|	cmp TMP1, FCARG1x
+		|	bne &exit_addr
+		if (ssa->var_info && ssa_op->op2_use >= 0) {
+			ssa->var_info[ssa_op->op2_use].type |= MAY_BE_CLASS_GUARD;
+			ssa->var_info[ssa_op->op2_use].ce = zend_ce_closure;
+			ssa->var_info[ssa_op->op2_use].is_instanceof = 0;
+		}
+	}
+
+	if (trace
+	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
+	 && trace->func
+	 && trace->func->type == ZEND_USER_FUNCTION) {
+		const zend_op *opcodes;
+		int32_t exit_point;
+		const void *exit_addr;
+
+		func = (zend_function*)trace->func;
+		opcodes = func->op_array.opcodes;
+		exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_CLOSURE_CALL);
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+		if (!exit_addr) {
+			return 0;
+		}
+
+		|	LOAD_ADDR FCARG1x, ((ptrdiff_t)opcodes)
+		|	ldr TMP1, [REG0, #offsetof(zend_closure, func.op_array.opcodes)]
+		|	cmp TMP1, FCARG1x
+		|	bne &exit_addr
+	}
+
+	if (delayed_call_chain) {
+		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
+			return 0;
+		}
+	}
+
+	if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 1, 0, stack_check)) {
+		return 0;
+	}
+
+	if (zend_jit_needs_call_chain(NULL, b, op_array, ssa, ssa_op, opline, trace)) {
+		if (!zend_jit_save_call_chain(Dst, call_level)) {
+			return 0;
+		}
+	} else {
+		delayed_call_chain = 1;
+		delayed_call_level = call_level;
+	}
+
+	if (trace
+	 && trace->op == ZEND_JIT_TRACE_END
+	 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
+		if (!zend_jit_set_valid_ip(Dst, opline + 1)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ssa, const zend_call_info *call_info)
+{
+	uint32_t num_args = 0;
+	zend_function *func = call_info->callee_func;
+
+	/* It's okay to handle prototypes here, because they can only increase the accepted arguments.
+	 * Anything legal for the parent method is also legal for the parent method. */
+	while (num_args < call_info->num_args) {
+		zend_arg_info *arg_info = func->op_array.arg_info + num_args;
+
+		if (ZEND_TYPE_IS_SET(arg_info->type)) {
+			if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) {
+				zend_op *opline = call_info->arg_info[num_args].opline;
+				zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
+				uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
+				if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) {
+					break;
+				}
+			} else {
+				break;
+			}
+		}
+		num_args++;
+	}
+	return num_args;
+}
+
+static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, int call_level, unsigned int next_block, zend_jit_trace_rec *trace)
+{
+	zend_func_info *info = ZEND_FUNC_INFO(op_array);
+	zend_call_info *call_info = NULL;
+	const zend_function *func = NULL;
+	uint32_t i;
+	zend_jit_addr res_addr;
+	uint32_t call_num_args = 0;
+	bool unknown_num_args = 0;
+	const void *exit_addr = NULL;
+	const zend_op *prev_opline;
+
+	if (RETURN_VALUE_USED(opline)) {
+		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+	} else {
+		/* CPU stack allocated temporary zval */
+		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RSP, TMP_ZVAL_OFFSET);
+	}
+
+	prev_opline = opline - 1;
+	while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) {
+		prev_opline--;
+	}
+	if (prev_opline->opcode == ZEND_SEND_UNPACK || prev_opline->opcode == ZEND_SEND_ARRAY ||
+			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
+		unknown_num_args = 1;
+	}
+
+	if (info) {
+		call_info = info->callee_info;
+		while (call_info && call_info->caller_call_opline != opline) {
+			call_info = call_info->next_callee;
+		}
+		if (call_info && call_info->callee_func && !call_info->is_prototype) {
+			func = call_info->callee_func;
+		}
+	}
+	if (!func) {
+		/* resolve function at run time */
+	} else if (func->type == ZEND_USER_FUNCTION) {
+		ZEND_ASSERT(opline->opcode != ZEND_DO_ICALL);
+		call_num_args = call_info->num_args;
+	} else if (func->type == ZEND_INTERNAL_FUNCTION) {
+		ZEND_ASSERT(opline->opcode != ZEND_DO_UCALL);
+		call_num_args = call_info->num_args;
+	} else {
+		ZEND_UNREACHABLE();
+	}
+
+	if (trace && !func) {
+		if (trace->op == ZEND_JIT_TRACE_DO_ICALL) {
+			ZEND_ASSERT(trace->func->type == ZEND_INTERNAL_FUNCTION);
+#ifndef ZEND_WIN32
+			// TODO: ASLR may cause different addresses in different workers ???
+			func = trace->func;
+			if (JIT_G(current_frame) &&
+			    JIT_G(current_frame)->call &&
+			    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
+				call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
+			} else {
+				unknown_num_args = 1;
+			}
+#endif
+		} else if (trace->op == ZEND_JIT_TRACE_ENTER) {
+			ZEND_ASSERT(trace->func->type == ZEND_USER_FUNCTION);
+			if (zend_accel_in_shm(trace->func->op_array.opcodes)) {
+				func = trace->func;
+				if (JIT_G(current_frame) &&
+				    JIT_G(current_frame)->call &&
+				    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
+					call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
+				} else {
+					unknown_num_args = 1;
+				}
+			}
+		}
+	}
+
+	bool may_have_extra_named_params =
+		opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS &&
+		(!func || func->common.fn_flags & ZEND_ACC_VARIADIC);
+
+	if (!reuse_ip) {
+		zend_jit_start_reuse_ip();
+		|	// call = EX(call);
+		|	ldr RX, EX->call
+	}
+	zend_jit_stop_reuse_ip();
+
+	|	// fbc = call->func;
+	|	// mov r2, EX:RX->func ???
+	|	// SAVE_OPLINE();
+	|	SET_EX_OPLINE opline, REG0
+
+	if (opline->opcode == ZEND_DO_FCALL) {
+		if (!func) {
+			if (trace) {
+				uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+
+				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+				if (!exit_addr) {
+					return 0;
+				}
+				|	ldr REG0, EX:RX->func
+				|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
+				|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
+				|	bne &exit_addr
+			}
+		}
+	}
+
+	if (!delayed_call_chain) {
+		if (call_level == 1) {
+			|	str xzr, EX->call
+		} else {
+			|	//EX(call) = call->prev_execute_data;
+			|	ldr REG0, EX:RX->prev_execute_data
+			|	str REG0, EX->call
+		}
+	}
+	delayed_call_chain = 0;
+
+	|	//call->prev_execute_data = execute_data;
+	|	str EX, EX:RX->prev_execute_data
+
+	if (!func) {
+		|	ldr REG0, EX:RX->func
+	}
+
+	if (opline->opcode == ZEND_DO_FCALL) {
+		if (!func) {
+			if (!trace) {
+				|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
+				|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
+				|	bne >1
+				|.cold_code
+				|1:
+				if (!GCC_GLOBAL_REGS) {
+					|	mov FCARG1x, RX
+				}
+				|	EXT_CALL zend_jit_deprecated_helper, REG0
+				|	GET_LOW_8BITS RETVALw, RETVALw
+				|	ldr REG0, EX:RX->func // reload
+				|	cbnz RETVALw, >1      // Result is 0 on exception
+				|	b ->exception_handler
+				|.code
+				|1:
+			}
+		} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
+			if (!GCC_GLOBAL_REGS) {
+				|	mov FCARG1x, RX
+			}
+			|	EXT_CALL zend_jit_deprecated_helper, REG0
+			|	cbz RETVALw, ->exception_handler
+		}
+	}
+
+	if (!func
+	 && opline->opcode != ZEND_DO_UCALL
+	 && opline->opcode != ZEND_DO_ICALL) {
+		|	ldrb TMP1w, [REG0, #offsetof(zend_function, type)]
+		|	cmp TMP1w, #ZEND_USER_FUNCTION
+		|	bne >8
+	}
+
+	if ((!func || func->type == ZEND_USER_FUNCTION)
+	 && opline->opcode != ZEND_DO_ICALL) {
+		|	// EX(call) = NULL;
+		|	str xzr, EX:RX->call
+
+		if (RETURN_VALUE_USED(opline)) {
+			|	// EX(return_value) = EX_VAR(opline->result.var);
+			|	LOAD_ZVAL_ADDR REG2, res_addr
+			|	str REG2, EX:RX->return_value
+		} else {
+			|	// EX(return_value) = 0;
+			|	str xzr, EX:RX->return_value
+		}
+
+		//EX_LOAD_RUN_TIME_CACHE(op_array);
+		if (!func || func->op_array.cache_size) {
+			if (func && op_array == &func->op_array) {
+				/* recursive call */
+				if (trace || func->op_array.cache_size > sizeof(void*)) {
+					|	ldr REG2, EX->run_time_cache
+					|	str REG2, EX:RX->run_time_cache
+				}
+			} else {
+				if (func) {
+					|	ldr REG0, EX:RX->func
+				}
+				|	ldr REG2, [REG0, #offsetof(zend_op_array, run_time_cache__ptr)]
+// Always defined as ZEND_MAP_PTR_KIND_PTR_OR_OFFSET. See Zend/zend_map_ptr.h.
+#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
+				|	ldr REG2, [REG2]
+#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
+				if (func && !(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
+					if (ZEND_MAP_PTR_IS_OFFSET(func->op_array.run_time_cache)) {
+						|	MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1
+					} else if ((func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
+					        && (!func->op_array.scope || (func->op_array.scope->ce_flags & ZEND_ACC_LINKED))) {
+						|	MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1
+					} else {
+						/* the called op_array may be not persisted yet */
+						|	TST_64_WITH_ONE REG2
+						|	beq >1
+						|	MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1
+						|1:
+					}
+					|	ldr REG2, [REG2]
+				} else {
+					|	TST_64_WITH_ONE REG2
+					|	beq >1
+					|	MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1
+					|1:
+					|	ldr REG2, [REG2]
+				}
+#else
+# error "Unknown ZEND_MAP_PTR_KIND"
+#endif
+				|	str REG2, EX:RX->run_time_cache
+			}
+		}
+
+		|	// EG(current_execute_data) = execute_data;
+		|	MEM_STORE_ZTS str, RX, executor_globals, current_execute_data, REG1
+		|	mov FP, RX
+
+		|	// opline = op_array->opcodes;
+		if (func && !unknown_num_args) {
+			|	ADD_SUB_64_WITH_CONST_32 add, TMP1, RX, (EX_NUM_TO_VAR(call_num_args) + offsetof(zval, u1.type_info)), TMP1 // induction variable
+			for (i = call_num_args; i < func->op_array.last_var; i++) {
+				|	// ZVAL_UNDEF(EX_VAR(n))
+				|	str wzr, [TMP1], #16
+			}
+
+			if (call_num_args <= func->op_array.num_args) {
+				if (!trace || (trace->op == ZEND_JIT_TRACE_END
+				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
+					uint32_t num_args;
+
+					if ((func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
+						if (trace) {
+							num_args = 0;
+						} else if (call_info) {
+							num_args = skip_valid_arguments(op_array, ssa, call_info);
+						} else {
+							num_args = call_num_args;
+						}
+					} else {
+						num_args = call_num_args;
+					}
+					if (zend_accel_in_shm(func->op_array.opcodes)) {
+						|	LOAD_IP_ADDR (func->op_array.opcodes + num_args)
+					} else {
+						|	ldr REG0, EX->func
+						||	ZEND_ASSERT((num_args * sizeof(zend_op)) <= ADD_SUB_IMM);
+						if (GCC_GLOBAL_REGS) {
+							|	ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
+							if (num_args) {
+								|	add IP, IP, #(num_args * sizeof(zend_op))
+							}
+						} else {
+							|	ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)]
+							if (num_args) {
+								|	add FCARG1x, FCARG1x, #(num_args * sizeof(zend_op))
+							}
+							|	str FCARG1x, EX->opline
+						}
+					}
+
+					if (!trace && op_array == &func->op_array) {
+						/* recursive call */
+						if (ZEND_OBSERVER_ENABLED) {
+							|	SAVE_IP
+							|	mov FCARG1x, FP
+							|	EXT_CALL zend_observer_fcall_begin, REG0
+						}
+#ifdef CONTEXT_THREADED_JIT
+						|	NIY	// TODO
+#else
+						|	b =>num_args
+#endif
+						return 1;
+					}
+				}
+			} else {
+				if (!trace || (trace->op == ZEND_JIT_TRACE_END
+				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
+					if (func && zend_accel_in_shm(func->op_array.opcodes)) {
+						|	LOAD_IP_ADDR (func->op_array.opcodes)
+					} else if (GCC_GLOBAL_REGS) {
+						|	ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
+					} else {
+						|	ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)]
+						|	str FCARG1x, EX->opline
+					}
+				}
+				if (!GCC_GLOBAL_REGS) {
+					|	mov FCARG1x, FP
+				}
+				|	EXT_CALL zend_jit_copy_extra_args_helper, REG0
+			}
+		} else {
+			|	// opline = op_array->opcodes
+			if (func && zend_accel_in_shm(func->op_array.opcodes)) {
+				|	LOAD_IP_ADDR (func->op_array.opcodes)
+			} else if (GCC_GLOBAL_REGS) {
+				|	ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
+			} else {
+				|	ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)]
+				|	str FCARG1x, EX->opline
+			}
+			if (func) {
+				|	// num_args = EX_NUM_ARGS();
+				|	ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)]
+				|	// if (UNEXPECTED(num_args > first_extra_arg))
+				|	CMP_32_WITH_CONST REG1w, (func->op_array.num_args), TMP1w
+			} else {
+				|	// first_extra_arg = op_array->num_args;
+				|	ldr REG2w, [REG0, #offsetof(zend_op_array, num_args)]
+				|	// num_args = EX_NUM_ARGS();
+				|	ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)]
+				|	// if (UNEXPECTED(num_args > first_extra_arg))
+				|	cmp REG1w, REG2w
+			}
+			|	bgt >1
+			|.cold_code
+			|1:
+			if (!GCC_GLOBAL_REGS) {
+				|	mov FCARG1x, FP
+			}
+			|	EXT_CALL zend_jit_copy_extra_args_helper, REG0
+			if (!func) {
+				|	ldr REG0, EX->func // reload
+			}
+			|	ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)] // reload
+			|	b >1
+			|.code
+			if (!func || (func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0) {
+				if (!func) {
+					|	// if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0))
+					|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
+					|	TST_32_WITH_CONST TMP1w, ZEND_ACC_HAS_TYPE_HINTS, TMP2w
+					|	bne >1
+				}
+				|	// opline += num_args;
+				||	ZEND_ASSERT(sizeof(zend_op) == 32);
+				|	mov REG2w, REG1w
+				|	ADD_IP_SHIFT REG2, lsl #5, TMP1
+			}
+			|1:
+			|	// if (EXPECTED((int)num_args < op_array->last_var)) {
+			if (func) {
+				|	LOAD_32BIT_VAL REG2w, func->op_array.last_var
+			} else {
+				|	ldr REG2w, [REG0, #offsetof(zend_op_array, last_var)]
+			}
+			|	subs REG2w, REG2w, REG1w
+			|	ble >3
+			|	// zval *var = EX_VAR_NUM(num_args);
+			|	add REG1, FP, REG1, lsl #4
+			||	ZEND_ASSERT(ZEND_CALL_FRAME_SLOT * sizeof(zval) <= ADD_SUB_IMM);
+			|	add REG1, REG1, #(ZEND_CALL_FRAME_SLOT * sizeof(zval))
+			|2:
+			|	SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w
+			|	add REG1, REG1, #16
+			|	subs REG2w, REG2w, #1
+			|	bne <2
+			|3:
+		}
+
+		if (ZEND_OBSERVER_ENABLED) {
+			|	SAVE_IP
+			|	mov FCARG1x, FP
+			|	EXT_CALL zend_observer_fcall_begin, REG0
+		}
+
+		if (trace) {
+			if (!func && (opline->opcode != ZEND_DO_UCALL)) {
+				|	b >9
+			}
+		} else {
+#ifdef CONTEXT_THREADED_JIT
+			|	NIY	// TODO: CONTEXT_THREADED_JIT is always undefined.
+#else
+			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+				|	ADD_HYBRID_SPAD
+				|	JMP_IP TMP1
+			} else if (GCC_GLOBAL_REGS) {
+				|	ldp x29, x30, [sp], # SPAD // stack alignment
+				|	JMP_IP TMP1
+			} else {
+				|	ldp FP, RX, T2                // retore FP and IP
+				|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+				|	mov RETVALx, #1     // ZEND_VM_ENTER
+				|	ret
+			}
+		}
+#endif
+	}
+
+	if ((!func || func->type == ZEND_INTERNAL_FUNCTION)
+	 && (opline->opcode != ZEND_DO_UCALL)) {
+		if (!func && (opline->opcode != ZEND_DO_ICALL)) {
+			|8:
+		}
+		if (opline->opcode == ZEND_DO_FCALL_BY_NAME) {
+			if (!func) {
+				if (trace) {
+					uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+
+					exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+					if (!exit_addr) {
+						return 0;
+					}
+					|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
+					|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
+					|	bne &exit_addr
+				} else {
+					|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
+					|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
+					|	bne >1
+					|.cold_code
+					|1:
+					if (!GCC_GLOBAL_REGS) {
+						|	mov FCARG1x, RX
+					}
+					|	EXT_CALL zend_jit_deprecated_helper, REG0
+					|	GET_LOW_8BITS RETVALw, RETVALw
+					|	ldr REG0, EX:RX->func // reload
+					|	cbnz RETVALw, >1      // Result is 0 on exception
+					|	b ->exception_handler
+					|.code
+					|1:
+				}
+			} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
+				if (!GCC_GLOBAL_REGS) {
+					|	mov FCARG1x, RX
+				}
+				|	EXT_CALL zend_jit_deprecated_helper, REG0
+				|	cbz RETVALw, ->exception_handler
+				|	ldr REG0, EX:RX->func // reload
+			}
+		}
+
+		|	// ZVAL_NULL(EX_VAR(opline->result.var));
+		|	LOAD_ZVAL_ADDR FCARG2x, res_addr
+		|	SET_Z_TYPE_INFO FCARG2x, IS_NULL, TMP1w
+
+		|	// EG(current_execute_data) = execute_data;
+		|	MEM_STORE_ZTS str, RX, executor_globals, current_execute_data, REG1
+
+		zend_jit_reset_last_valid_opline();
+
+		|	// fbc->internal_function.handler(call, ret);
+		|	mov FCARG1x, RX
+		if (func) {
+			|	EXT_CALL func->internal_function.handler, REG0
+		} else {
+			|	ldr TMP1, [REG0, #offsetof(zend_internal_function, handler)]
+			|	blr TMP1
+		}
+
+		|	// EG(current_execute_data) = execute_data;
+		|	MEM_STORE_ZTS str, FP, executor_globals, current_execute_data, REG0
+
+		|	// zend_vm_stack_free_args(call);
+		if (func && !unknown_num_args) {
+			for (i = 0; i < call_num_args; i++ ) {
+				uint32_t offset = EX_NUM_TO_VAR(i);
+				zend_jit_addr arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, offset);
+				|	ZVAL_PTR_DTOR arg_addr, (MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN), 0, 1, opline, ZREG_TMP1, ZREG_TMP2
+			}
+		} else {
+			|	mov FCARG1x, RX
+			|	EXT_CALL zend_jit_vm_stack_free_args_helper, REG0
+		}
+		if (may_have_extra_named_params) {
+		    |	ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 3)]
+			|	TST_32_WITH_CONST TMP1w, (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS >> 24), TMP2w
+			|	bne >1
+			|.cold_code
+			|1:
+			|	ldr FCARG1x, [RX, #offsetof(zend_execute_data, extra_named_params)]
+			|	EXT_CALL zend_free_extra_named_params, REG0
+			|	b >2
+			|.code
+			|2:
+		}
+
+		|8:
+		if (opline->opcode == ZEND_DO_FCALL) {
+			// TODO: optimize ???
+			|	// if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS))
+			|	ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 2)]
+			|	TST_32_WITH_CONST TMP1w, (ZEND_CALL_RELEASE_THIS >> 16), TMP2w
+			|	bne >1
+			|.cold_code
+			|1:
+			|	add TMP1, RX, #offsetof(zend_execute_data, This)
+			|	GET_Z_PTR FCARG1x, TMP1
+			|	// OBJ_RELEASE(object);
+			|	OBJ_RELEASE ZREG_FCARG1x, >2, ZREG_TMP1, ZREG_TMP2
+			|	b >2
+			|.code
+			|2:
+		}
+
+		if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
+		    !JIT_G(current_frame) ||
+		    !JIT_G(current_frame)->call ||
+		    !TRACE_FRAME_IS_NESTED(JIT_G(current_frame)->call) ||
+		    prev_opline->opcode == ZEND_SEND_UNPACK ||
+		    prev_opline->opcode == ZEND_SEND_ARRAY ||
+			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
+
+			|	// zend_vm_stack_free_call_frame(call);
+			|	ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 2)]
+			|	TST_32_WITH_CONST TMP1w, ((ZEND_CALL_ALLOCATED >> 16) & 0xff), TMP2w
+			|	bne >1
+			|.cold_code
+			|1:
+			|	mov FCARG1x, RX
+			|	EXT_CALL zend_jit_free_call_frame, REG0
+			|	b >1
+			|.code
+		}
+		|	MEM_STORE_ZTS str, RX, executor_globals, vm_stack_top, REG0
+		|1:
+
+		if (!RETURN_VALUE_USED(opline)) {
+			zend_class_entry *ce;
+			bool ce_is_instanceof;
+			uint32_t func_info = call_info ?
+				zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof) :
+				(MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
+
+			/* If an exception is thrown, the return_value may stay at the
+			 * original value of null. */
+			func_info |= MAY_BE_NULL;
+
+			if (func_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+				|	ZVAL_PTR_DTOR res_addr, func_info, 1, 1, opline, ZREG_TMP1, ZREG_TMP2
+			}
+		}
+
+		|	// if (UNEXPECTED(EG(exception) != NULL)) {
+		|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+		|	cbnz REG0, ->icall_throw_handler
+
+		// TODO: Can we avoid checking for interrupts after each call ???
+		if (trace && last_valid_opline != opline) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM);
+
+			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+			if (!exit_addr) {
+				return 0;
+			}
+		} else {
+			exit_addr = NULL;
+		}
+		if (!zend_jit_check_timeout(Dst, opline + 1, exit_addr)) {
+			return 0;
+		}
+
+		if ((!trace || !func) && opline->opcode != ZEND_DO_ICALL) {
+			|	LOAD_IP_ADDR (opline + 1)
+		} else if (trace
+		 && trace->op == ZEND_JIT_TRACE_END
+		 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
+			|	LOAD_IP_ADDR (opline + 1)
+		}
+	}
+
+	if (!func) {
+		|9:
+	}
+
+	return 1;
+}
+
+static int zend_jit_send_val(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr)
+{
+	uint32_t arg_num = opline->op2.num;
+	zend_jit_addr arg_addr;
+
+	ZEND_ASSERT(opline->opcode == ZEND_SEND_VAL || arg_num <= MAX_ARG_FLAG_NUM);
+
+	if (!zend_jit_reuse_ip(Dst)) {
+		return 0;
+	}
+
+	if (opline->opcode == ZEND_SEND_VAL_EX) {
+		uint32_t mask = ZEND_SEND_BY_REF << ((arg_num + 3) * 2);
+
+		ZEND_ASSERT(arg_num <= MAX_ARG_FLAG_NUM);
+
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
+		 && JIT_G(current_frame)
+		 && JIT_G(current_frame)->call
+		 && JIT_G(current_frame)->call->func) {
+			if (ARG_MUST_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
+				/* Don't generate code that always throws exception */
+				return 0;
+			}
+		} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+			if (!exit_addr) {
+				return 0;
+			}
+			|	ldr REG0, EX:RX->func
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
+			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
+			|	bne &exit_addr
+		} else {
+			|	ldr REG0, EX:RX->func
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
+			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
+			|	bne >1
+			|.cold_code
+			|1:
+			|	SET_EX_OPLINE opline, REG0
+			|	b ->throw_cannot_pass_by_ref
+			|.code
+		}
+	}
+
+	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
+
+	if (opline->op1_type == IS_CONST) {
+		zval *zv = RT_CONSTANT(opline, opline->op1);
+
+		|	ZVAL_COPY_CONST arg_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
+		if (Z_REFCOUNTED_P(zv)) {
+			|	ADDREF_CONST zv, REG0, TMP1
+		}
+	} else {
+		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+	}
+
+	return 1;
+}
+
+static int zend_jit_check_undef_args(dasm_State **Dst, const zend_op *opline)
+{
+	|	ldr FCARG1x, EX->call
+	|	ldrb TMP1w, [FCARG1x, #(offsetof(zend_execute_data, This.u1.type_info) + 3)]
+	|	TST_32_WITH_CONST TMP1w, (ZEND_CALL_MAY_HAVE_UNDEF >> 24), TMP2w
+	|	bne >1
+	|.cold_code
+	|1:
+	|	SET_EX_OPLINE opline, REG0
+	|	EXT_CALL zend_handle_undef_args, REG0
+	|	cbz RETVALw, >2
+	|	b ->exception_handler
+	|.code
+	|2:
+
+	return 1;
+}
+
+static int zend_jit_send_ref(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, int cold)
+{
+	zend_jit_addr op1_addr, arg_addr, ref_addr;
+
+	op1_addr = OP1_ADDR();
+	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
+
+	if (!zend_jit_reuse_ip(Dst)) {
+		return 0;
+	}
+
+	if (opline->op1_type == IS_VAR) {
+		if (op1_info & MAY_BE_INDIRECT) {
+			|	LOAD_ZVAL_ADDR REG0, op1_addr
+			|	// if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) {
+			|	IF_NOT_Z_TYPE REG0, IS_INDIRECT, >1, TMP1w
+			|	// ret = Z_INDIRECT_P(ret);
+			|	GET_Z_PTR REG0, REG0
+			|1:
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+		}
+	} else if (opline->op1_type == IS_CV) {
+		if (op1_info & MAY_BE_UNDEF) {
+			if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2
+				|	b >2
+				|1:
+			}
+			op1_info &= ~MAY_BE_UNDEF;
+			op1_info |= MAY_BE_NULL;
+		}
+	} else {
+		ZEND_UNREACHABLE();
+	}
+
+	if (op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) {
+		if (op1_info & MAY_BE_REF) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >2, ZREG_TMP1
+			|	GET_ZVAL_PTR REG1, op1_addr, TMP1
+			|	GC_ADDREF REG1, TMP1w
+			|	SET_ZVAL_PTR arg_addr, REG1, TMP1
+			|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX, TMP1w, TMP2
+			|	b >6
+		}
+		|2:
+		|	// ZVAL_NEW_REF(arg, varptr);
+		if (opline->op1_type == IS_VAR) {
+			if (Z_REG(op1_addr) != ZREG_REG0 || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR REG0, op1_addr
+			}
+			|	str REG0, T1  // save
+		}
+		|	EMALLOC sizeof(zend_reference), op_array, opline  // Allocate space in REG0
+		|	mov TMP1w, #2
+		|	str TMP1w, [REG0]
+		||	ZEND_ASSERT(GC_REFERENCE <= MOVZ_IMM);
+		|	movz TMP1w, #GC_REFERENCE
+		|	str TMP1w, [REG0, #offsetof(zend_reference, gc.u.type_info)]
+		|	str xzr, [REG0, #offsetof(zend_reference, sources.ptr)]
+		ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, offsetof(zend_reference, val));
+		if (opline->op1_type == IS_VAR) {
+			zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG1, 0);
+
+			|	ldr REG1, T1  // restore
+			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_REG2, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			|	SET_ZVAL_PTR val_addr, REG0, TMP1
+			|	SET_ZVAL_TYPE_INFO val_addr, IS_REFERENCE_EX, TMP1w, TMP2
+		} else {
+			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			|	SET_ZVAL_PTR op1_addr, REG0, TMP1
+			|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2
+		}
+		|	SET_ZVAL_PTR arg_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX, TMP1w, TMP2
+	}
+
+	|6:
+	|	FREE_OP opline->op1_type, opline->op1, op1_info, !cold, opline, ZREG_TMP1, ZREG_TMP2
+	|7:
+
+	return 1;
+}
+
+static int zend_jit_send_var(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr op1_def_addr)
+{
+	uint32_t arg_num = opline->op2.num;
+	zend_jit_addr arg_addr;
+
+	ZEND_ASSERT((opline->opcode != ZEND_SEND_VAR_EX &&
+	     opline->opcode != ZEND_SEND_VAR_NO_REF_EX) ||
+	    arg_num <= MAX_ARG_FLAG_NUM);
+
+	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
+
+	if (!zend_jit_reuse_ip(Dst)) {
+		return 0;
+	}
+
+	if (opline->opcode == ZEND_SEND_VAR_EX) {
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
+		 && JIT_G(current_frame)
+		 && JIT_G(current_frame)->call
+		 && JIT_G(current_frame)->call->func) {
+			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
+				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
+					return 0;
+				}
+				return 1;
+			}
+		} else {
+			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
+
+			|	ldr REG0, EX:RX->func
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
+			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
+			|	bne >1
+			|.cold_code
+			|1:
+			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
+				return 0;
+			}
+			|	b >7
+			|.code
+		}
+	} else if (opline->opcode == ZEND_SEND_VAR_NO_REF_EX) {
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
+		 && JIT_G(current_frame)
+		 && JIT_G(current_frame)->call
+		 && JIT_G(current_frame)->call->func) {
+			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
+
+				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+
+				if (!ARG_MAY_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
+					if (!(op1_info & MAY_BE_REF)) {
+						/* Don't generate code that always throws exception */
+						return 0;
+					} else {
+						int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+						const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+						if (!exit_addr) {
+							return 0;
+						}
+						|	GET_LOW_8BITS TMP1w, REG1w
+						|	cmp TMP1w, #IS_REFERENCE
+						|	bne &exit_addr
+					}
+				}
+				return 1;
+			}
+		} else {
+			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
+
+			|	ldr REG0, EX:RX->func
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
+			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
+			|	bne >1
+			|.cold_code
+			|1:
+
+			mask = ZEND_SEND_PREFER_REF << ((arg_num + 3) * 2);
+
+			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			if (op1_info & MAY_BE_REF) {
+				|	GET_LOW_8BITS TMP1w, REG1w
+				|	cmp TMP1w, #IS_REFERENCE
+				|	beq >7
+			}
+			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
+			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
+			|	bne >7
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+				if (!exit_addr) {
+					return 0;
+				}
+				|	b &exit_addr
+			} else {
+				|	SET_EX_OPLINE opline, REG0
+				|	LOAD_ZVAL_ADDR FCARG1x, arg_addr
+				|	EXT_CALL zend_jit_only_vars_by_reference, REG0
+				if (!zend_jit_check_exception(Dst)) {
+					return 0;
+				}
+				|	b >7
+			}
+
+			|.code
+		}
+	} else if (opline->opcode == ZEND_SEND_FUNC_ARG) {
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
+		 && JIT_G(current_frame)
+		 && JIT_G(current_frame)->call
+		 && JIT_G(current_frame)->call->func) {
+			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
+				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
+					return 0;
+				}
+				return 1;
+			}
+		} else {
+			|	ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+			|	TST_32_WITH_CONST TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
+			|	bne >1
+			|.cold_code
+			|1:
+			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
+				return 0;
+			}
+			|	b >7
+			|.code
+		}
+	}
+
+	if (op1_info & MAY_BE_UNDEF) {
+		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
+			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+			|.cold_code
+			|1:
+		}
+
+		|	SET_EX_OPLINE opline, REG0
+		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+		|	EXT_CALL zend_jit_undefined_op_helper, REG0
+		|	SET_ZVAL_TYPE_INFO arg_addr, IS_NULL, TMP1w, TMP2
+		|	cbz RETVALx, ->exception_handler
+
+		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
+			|	b >7
+			|.code
+		} else {
+			|7:
+			return 1;
+		}
+	}
+
+	if (opline->opcode == ZEND_SEND_VAR_NO_REF) {
+		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		if (op1_info & MAY_BE_REF) {
+			|	GET_LOW_8BITS TMP1w, REG1w
+			|	cmp TMP1w, #IS_REFERENCE
+			|	beq >7
+		}
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+			if (!exit_addr) {
+				return 0;
+			}
+			|	b &exit_addr
+		} else {
+			|	SET_EX_OPLINE opline, REG0
+			|	LOAD_ZVAL_ADDR FCARG1x, arg_addr
+			|	EXT_CALL zend_jit_only_vars_by_reference, REG0
+			if (!zend_jit_check_exception(Dst)) {
+				return 0;
+			}
+		}
+	} else {
+		if (op1_info & MAY_BE_REF) {
+			if (opline->op1_type == IS_CV) {
+				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+				|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
+			} else {
+				zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 8);
+
+				|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1
+				|.cold_code
+				|1:
+				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
+				|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+				|	// ZVAL_COPY_VALUE(return_value, &ref->value);
+				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+				|	GC_DELREF FCARG1x, TMP1w
+				|	beq >1
+				|	IF_NOT_REFCOUNTED REG0w, >2, TMP1w
+				|	GC_ADDREF REG2, TMP1w
+				|	b >2
+				|1:
+				|	EFREE_REFERENCE
+				|	b >2
+				|.code
+				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+				|2:
+			}
+		} else {
+			if (op1_addr != op1_def_addr) {
+				if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
+					return 0;
+				}
+				if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
+					op1_addr= op1_def_addr;
+				}
+			}
+			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			if (opline->op1_type == IS_CV) {
+				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
+			}
+		}
+	}
+	|7:
+
+	return 1;
+}
+
+static int zend_jit_check_func_arg(dasm_State **Dst, const zend_op *opline)
+{
+	uint32_t arg_num = opline->op2.num;
+
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
+	 && JIT_G(current_frame)
+	 && JIT_G(current_frame)->call
+	 && JIT_G(current_frame)->call->func) {
+		if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
+			if (!TRACE_FRAME_IS_LAST_SEND_BY_REF(JIT_G(current_frame)->call)) {
+				TRACE_FRAME_SET_LAST_SEND_BY_REF(JIT_G(current_frame)->call);
+				|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+				||	if (reuse_ip) {
+				|		ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+				|		BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
+				|		str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+				||	} else {
+				|		ldr REG0, EX->call
+				|		ldr TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
+				|		BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
+				|		str TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
+				||	}
+			}
+		} else {
+			if (!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
+				TRACE_FRAME_SET_LAST_SEND_BY_VAL(JIT_G(current_frame)->call);
+				|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+				||	if (reuse_ip) {
+				|		ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+				|		BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~ZEND_CALL_SEND_ARG_BY_REF), TMP2w
+				|		str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+				||	} else {
+				|		ldr REG0, EX->call
+				|		ldr TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
+				|		BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~ZEND_CALL_SEND_ARG_BY_REF), TMP2w
+				|		str TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
+				||	}
+			}
+		}
+	} else {
+		// if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+		uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
+
+		if (!zend_jit_reuse_ip(Dst)) {
+			return 0;
+		}
+
+		|	ldr REG0, EX:RX->func
+		|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
+		|	TST_32_WITH_CONST TMP1w, mask, TMP2w
+		|	bne >1
+		|.cold_code
+		|1:
+		|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+		|	ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+		|	BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
+		|	str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+		|	b >1
+		|.code
+		|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
+		|	ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+		|	BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~(ZEND_CALL_SEND_ARG_BY_REF)), TMP2w
+		|	str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
+		|1:
+	}
+
+	return 1;
+}
+
+static int zend_jit_smart_true(dasm_State **Dst, const zend_op *opline, int jmp, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2)
+{
+	if (smart_branch_opcode) {
+		if (smart_branch_opcode == ZEND_JMPZ) {
+			if (jmp) {
+				|	b >7
+			}
+		} else if (smart_branch_opcode == ZEND_JMPNZ) {
+			|	b =>target_label
+		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+			|	b =>target_label2
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	} else {
+		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+		if (jmp) {
+			|	b >7
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_smart_false(dasm_State **Dst, const zend_op *opline, int jmp, zend_uchar smart_branch_opcode, uint32_t target_label)
+{
+	if (smart_branch_opcode) {
+		if (smart_branch_opcode == ZEND_JMPZ) {
+			|	b =>target_label
+		} else if (smart_branch_opcode == ZEND_JMPNZ) {
+			if (jmp) {
+				|	b >7
+			}
+		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+			|	b =>target_label
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	} else {
+		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+		if (jmp) {
+			|	b >7
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_defined(dasm_State **Dst, const zend_op *opline, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	uint32_t defined_label = (uint32_t)-1;
+	uint32_t undefined_label = (uint32_t)-1;
+	zval *zv = RT_CONSTANT(opline, opline->op1);
+	zend_jit_addr res_addr = 0;
+
+	if (smart_branch_opcode && !exit_addr) {
+		if (smart_branch_opcode == ZEND_JMPZ) {
+			undefined_label = target_label;
+		} else if (smart_branch_opcode == ZEND_JMPNZ) {
+			defined_label = target_label;
+		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+			undefined_label = target_label;
+			defined_label = target_label2;
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	}
+
+	|	// if (CACHED_PTR(opline->extended_value)) {
+	|	ldr REG0, EX->run_time_cache
+	|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, opline->extended_value, TMP1	
+	|	cbz REG0, >1
+	|	TST_64_WITH_ONE REG0
+	|	bne >4
+	|.cold_code
+	|4:
+	|	MEM_LOAD_ZTS ldr, FCARG1x, executor_globals, zend_constants, FCARG1x
+	|	lsr REG0, REG0, #1
+	|	ldr TMP1w, [FCARG1x, #offsetof(HashTable, nNumOfElements)]
+	|	cmp TMP1, REG0
+
+	if (smart_branch_opcode) {
+		if (exit_addr) {
+			if (smart_branch_opcode == ZEND_JMPZ) {
+				|	beq &exit_addr
+			} else {
+				|	beq >3
+			}
+		} else if (undefined_label != (uint32_t)-1) {
+			|	beq =>undefined_label
+		} else {
+			|	beq >3
+		}
+	} else {
+		|	beq >2
+	}
+	|1:
+	|	SET_EX_OPLINE opline, REG0
+	|	LOAD_ADDR FCARG1x, zv
+	|	EXT_CALL zend_jit_check_constant, REG0
+	if (exit_addr) {
+		if (smart_branch_opcode == ZEND_JMPNZ) {
+			|	cbz RETVALx, >3
+		} else {
+			|	cbnz RETVALx, >3
+		}
+		|	b &exit_addr
+	} else if (smart_branch_opcode) {
+		if (undefined_label != (uint32_t)-1) {
+			|	cbz RETVALx, =>undefined_label
+		} else {
+			|	cbz RETVALx, >3
+		}
+		if (defined_label != (uint32_t)-1) {
+			|	b =>defined_label
+		} else {
+			|	b >3
+		}
+	} else {
+		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+		|	cbnz RETVALx, >1
+		|2:
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+		|	b >3
+	}
+	|.code
+	if (smart_branch_opcode) {
+		if (exit_addr) {
+			if (smart_branch_opcode == ZEND_JMPNZ) {
+				|	b &exit_addr
+			}
+		} else if (defined_label != (uint32_t)-1) {
+			|	b =>defined_label
+		}
+	} else {
+		|1:
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+	}
+	|3:
+
+	return 1;
+}
+
+static int zend_jit_type_check(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	uint32_t  mask;
+	zend_jit_addr op1_addr = OP1_ADDR();
+
+	// TODO: support for is_resource() ???
+	ZEND_ASSERT(opline->extended_value != MAY_BE_RESOURCE);
+
+	if (op1_info & MAY_BE_UNDEF) {
+		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
+			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+			|.cold_code
+			|1:
+		}
+		|	SET_EX_OPLINE opline, REG0
+		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+		|	EXT_CALL zend_jit_undefined_op_helper, REG0
+		zend_jit_check_exception_undef_result(Dst, opline);
+		if (opline->extended_value & MAY_BE_NULL) {
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPNZ) {
+					|	b &exit_addr
+				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
+					|	b >7
+				}
+			} else if (!zend_jit_smart_true(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label, target_label2)) {
+				return 0;
+			}
+		} else {
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPZ) {
+					|	b &exit_addr
+				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
+					|	b >7
+				}
+			} else if (!zend_jit_smart_false(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label)) {
+				return 0;
+			}
+		}
+		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
+			|.code
+		}
+	}
+
+	if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
+		mask = opline->extended_value;
+		if (!(op1_info & MAY_BE_GUARD) && !(op1_info & (MAY_BE_ANY - mask))) {
+			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPNZ) {
+					|	b &exit_addr
+				}
+			} else if (!zend_jit_smart_true(Dst, opline, 0, smart_branch_opcode, target_label, target_label2)) {
+				return 0;
+			}
+	    } else if (!(op1_info & MAY_BE_GUARD) && !(op1_info & mask)) {
+			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPZ) {
+					|	b &exit_addr
+				}
+			} else if (!zend_jit_smart_false(Dst, opline, 0, smart_branch_opcode, target_label)) {
+				return 0;
+			}
+		} else {
+			bool invert = 0;
+			zend_uchar type;
+
+			switch (mask) {
+				case MAY_BE_NULL:   type = IS_NULL;   break;
+				case MAY_BE_FALSE:  type = IS_FALSE;  break;
+				case MAY_BE_TRUE:   type = IS_TRUE;   break;
+				case MAY_BE_LONG:   type = IS_LONG;   break;
+				case MAY_BE_DOUBLE: type = IS_DOUBLE; break;
+				case MAY_BE_STRING: type = IS_STRING; break;
+				case MAY_BE_ARRAY:  type = IS_ARRAY;  break;
+				case MAY_BE_OBJECT: type = IS_OBJECT; break;
+				case MAY_BE_ANY - MAY_BE_NULL:     type = IS_NULL;   invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_FALSE:    type = IS_FALSE;  invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_TRUE:     type = IS_TRUE;   invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_LONG:     type = IS_LONG;   invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_DOUBLE:   type = IS_DOUBLE; invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_STRING:   type = IS_STRING; invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_ARRAY:    type = IS_ARRAY;  invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_OBJECT:   type = IS_OBJECT; invert = 1; break;
+				case MAY_BE_ANY - MAY_BE_RESOURCE: type = IS_OBJECT; invert = 1; break;
+				default:
+					type = 0;
+			}
+
+			if (op1_info & MAY_BE_REF) {
+				|	LOAD_ZVAL_ADDR REG0, op1_addr
+				|	ZVAL_DEREF REG0, op1_info, TMP1w
+			}
+			if (type == 0) {
+				if (smart_branch_opcode &&
+				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+						|	// if (Z_REFCOUNTED_P(cv)) {
+						|	IF_ZVAL_REFCOUNTED op1_addr, >1, ZREG_TMP1, ZREG_TMP2
+						|.cold_code
+						|1:
+					}
+					|	// if (!Z_DELREF_P(cv)) {
+					|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+					|	GC_DELREF FCARG1x, TMP1w
+					if (RC_MAY_BE_1(op1_info)) {
+						if (RC_MAY_BE_N(op1_info)) {
+							|	bne >3
+						}
+						if (op1_info & MAY_BE_REF) {
+							|	ldrb REG0w, [REG0, #offsetof(zval,u1.v.type)]
+						} else {
+							|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, REG0w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
+						}
+						|	str REG0w, T1 // save
+						|	// zval_dtor_func(r);
+						|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
+						|	ldr REG1w, T1 // restore
+						|	b >2
+					}
+					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+						if (!RC_MAY_BE_1(op1_info)) {
+							|	b >3
+						}
+						|.code
+					}
+					|3:
+					if (op1_info & MAY_BE_REF) {
+						|	ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)]
+					} else {
+						|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
+					}
+					|2:
+				} else {
+					if (op1_info & MAY_BE_REF) {
+						|	ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)]
+					} else {
+						|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
+					}
+				}
+				|	mov REG0w, #1
+				|	lsl REG0w, REG0w, REG1w
+				|	TST_32_WITH_CONST REG0w, mask, TMP1w
+				if (exit_addr) {
+					if (smart_branch_opcode == ZEND_JMPNZ) {
+						|	bne &exit_addr
+					} else {
+						|	beq &exit_addr
+					}
+				} else if (smart_branch_opcode) {
+					if (smart_branch_opcode == ZEND_JMPZ) {
+						|	beq =>target_label
+					} else if (smart_branch_opcode == ZEND_JMPNZ) {
+						|	bne =>target_label
+					} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+						|	beq =>target_label
+						|	b =>target_label2
+					} else {
+						ZEND_UNREACHABLE();
+					}
+				} else {
+					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+					|	cset REG0w, ne
+					|	add REG0w, REG0w, #2
+					|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+				}
+			} else {
+				if (smart_branch_opcode &&
+				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+						|	// if (Z_REFCOUNTED_P(cv)) {
+						|	IF_ZVAL_REFCOUNTED op1_addr, >1, ZREG_TMP1, ZREG_TMP2
+						|.cold_code
+						|1:
+					}
+					|	// if (!Z_DELREF_P(cv)) {
+					|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+					|	GC_DELREF FCARG1x, TMP1w
+					if (RC_MAY_BE_1(op1_info)) {
+						if (RC_MAY_BE_N(op1_info)) {
+							|	bne >3
+						}
+						if (op1_info & MAY_BE_REF) {
+							|	ldrb REG0w, [REG0, #offsetof(zval,u1.v.type)]
+						} else {
+							|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, REG0w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
+						}
+						|	str REG0w, T1 // save
+						|	// zval_dtor_func(r);
+						|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
+						|	ldr REG1w, T1 // restore
+						| 	b >2
+					}
+					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+						if (!RC_MAY_BE_1(op1_info)) {
+							|	b >3
+						}
+						|.code
+					}
+					|3:
+					if (op1_info & MAY_BE_REF) {
+						|	ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)]
+					} else {
+						|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
+					}
+					|2:
+					// Note: 'type' is of uchar type and holds a positive value,
+					// hence it's safe to directly encode it as the imm field of 'cmp' instruction.
+					|	cmp REG1w, #type
+				} else {
+					if (op1_info & MAY_BE_REF) {
+						|	ldrb TMP1w, [REG0, #offsetof(zval,u1.v.type)]
+						|	cmp TMP1w, #type
+					} else {
+						|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
+						|	cmp TMP1w, #type
+					}
+				}
+				if (exit_addr) {
+					if (invert) {
+						if (smart_branch_opcode == ZEND_JMPNZ) {
+							|	bne &exit_addr
+						} else {
+							|	beq &exit_addr
+						}
+					} else {
+						if (smart_branch_opcode == ZEND_JMPNZ) {
+							|	beq &exit_addr
+						} else {
+							|	bne &exit_addr
+						}
+					}
+				} else if (smart_branch_opcode) {
+					if (invert) {
+						if (smart_branch_opcode == ZEND_JMPZ) {
+							|	beq =>target_label
+						} else if (smart_branch_opcode == ZEND_JMPNZ) {
+							|	bne =>target_label
+						} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+							|	beq =>target_label
+							|	b =>target_label2
+						} else {
+							ZEND_UNREACHABLE();
+						}
+					} else {
+						if (smart_branch_opcode == ZEND_JMPZ) {
+							|	bne =>target_label
+						} else if (smart_branch_opcode == ZEND_JMPNZ) {
+							|	beq =>target_label
+						} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+							|	bne =>target_label
+							|	b =>target_label2
+						} else {
+							ZEND_UNREACHABLE();
+						}
+					}
+				} else {
+					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+					if (invert) {
+						|	cset REG0w, ne
+					} else {
+						|	cset REG0w, eq
+					}
+					|	add REG0w, REG0w, #2
+					|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+				}
+			}
+	    }
+	}
+
+	|7:
+
+	return 1;
+}
+
+static uint32_t zend_ssa_cv_info(const zend_op_array *op_array, zend_ssa *ssa, uint32_t var)
+{
+	uint32_t j, info;
+
+	if (ssa->vars && ssa->var_info) {
+		info = ssa->var_info[var].type;
+		for (j = op_array->last_var; j < ssa->vars_count; j++) {
+			if (ssa->vars[j].var == var) {
+				info |= ssa->var_info[j].type;
+			}
+		}
+	} else {
+		info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF |
+			MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+	}
+
+#ifdef ZEND_JIT_USE_RC_INFERENCE
+	/* Refcount may be increased by RETURN opcode */
+	if ((info & MAY_BE_RC1) && !(info & MAY_BE_RCN)) {
+		for (j = 0; j < ssa->cfg.blocks_count; j++) {
+			if ((ssa->cfg.blocks[j].flags & ZEND_BB_REACHABLE) &&
+			    ssa->cfg.blocks[j].len > 0) {
+				const zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].start + ssa->cfg.blocks[j].len - 1;
+
+				if (opline->opcode == ZEND_RETURN) {
+					if (opline->op1_type == IS_CV && opline->op1.var == EX_NUM_TO_VAR(var)) {
+						info |= MAY_BE_RCN;
+						break;
+					}
+				}
+			}
+		}
+	}
+#endif
+
+	return info;
+}
+
+static int zend_jit_leave_frame(dasm_State **Dst)
+{
+	|	// EG(current_execute_data) = EX(prev_execute_data);
+	|	ldr REG0, EX->prev_execute_data
+	|	MEM_STORE_ZTS str, REG0, executor_globals, current_execute_data, REG2
+	return 1;
+}
+
+static int zend_jit_free_cv(dasm_State **Dst, uint32_t info, uint32_t var)
+{
+	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+		uint32_t offset = EX_NUM_TO_VAR(var);
+		zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offset);
+		|	ZVAL_PTR_DTOR addr, info, 1, 1, NULL, ZREG_TMP1, ZREG_TMP2
+	}
+	return 1;
+}
+
+static int zend_jit_free_op(dasm_State **Dst, const zend_op *opline, uint32_t info, uint32_t var_offset)
+{
+	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+		zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var_offset);
+		| ZVAL_PTR_DTOR addr, info, 0, 1, opline, ZREG_TMP1, ZREG_TMP2
+	}
+	return 1;
+}
+
+static int zend_jit_leave_func(dasm_State          **Dst,
+                               const zend_op_array  *op_array,
+                               const zend_op        *opline,
+                               uint32_t              op1_info,
+                               bool                  left_frame,
+                               zend_jit_trace_rec   *trace,
+                               zend_jit_trace_info  *trace_info,
+                               int                   indirect_var_access,
+                               int                   may_throw)
+{
+	bool may_be_top_frame =
+		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
+		!JIT_G(current_frame) ||
+		!TRACE_FRAME_IS_NESTED(JIT_G(current_frame));
+	bool may_need_call_helper =
+		indirect_var_access || /* may have symbol table */
+		!op_array->function_name || /* may have symbol table */
+		may_be_top_frame ||
+		(op_array->fn_flags & ZEND_ACC_VARIADIC) || /* may have extra named args */
+		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
+		!JIT_G(current_frame) ||
+		TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) == -1 || /* unknown number of args */
+		(uint32_t)TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) > op_array->num_args; /* extra args */
+	bool may_need_release_this =
+		!(op_array->fn_flags & ZEND_ACC_CLOSURE) &&
+		op_array->scope &&
+		!(op_array->fn_flags & ZEND_ACC_STATIC) &&
+		(JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
+		 !JIT_G(current_frame) ||
+		 !TRACE_FRAME_NO_NEED_REKEASE_THIS(JIT_G(current_frame)));
+
+	if (may_need_call_helper || may_need_release_this) {
+		|	ldr FCARG1w, [FP, #offsetof(zend_execute_data, This.u1.type_info)]
+	}
+	if (may_need_call_helper) {
+		if (!left_frame) {
+			left_frame = 1;
+		    if (!zend_jit_leave_frame(Dst)) {
+				return 0;
+		    }
+		}
+		/* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */
+
+		|	TST_32_WITH_CONST FCARG1w, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_FAKE_CLOSURE), TMP1w
+		if (trace && trace->op != ZEND_JIT_TRACE_END) {
+			|	bne >1
+			|.cold_code
+			|1:
+			if (!GCC_GLOBAL_REGS) {
+				|	mov FCARG2x, FP
+			}
+			|	EXT_CALL zend_jit_leave_func_helper, REG0
+
+			if (may_be_top_frame) {
+				// TODO: try to avoid this check ???
+				if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+#if 0
+					/* this check should be handled by the following OPLINE guard */
+					|	LOAD_ADDR TMP1, zend_jit_halt_op
+					|	cmp IP, TMP1
+					|	beq ->trace_halt
+#endif
+				} else if (GCC_GLOBAL_REGS) {
+					|	cbz IP, ->trace_halt
+				} else {
+					|	tst RETVALw, RETVALw
+					|	blt ->trace_halt
+				}
+			}
+
+			if (!GCC_GLOBAL_REGS) {
+				|	// execute_data = EG(current_execute_data)
+				|	MEM_LOAD_ZTS ldr, FP, executor_globals, current_execute_data, TMP1
+			}
+			|	b >8
+			|.code
+		} else {
+			|	bne ->leave_function_handler
+		}
+	}
+
+	if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
+		if (!left_frame) {
+			left_frame = 1;
+		    if (!zend_jit_leave_frame(Dst)) {
+				return 0;
+		    }
+		}
+		|	// OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
+		|	ldr FCARG1x, EX->func
+		|	sub FCARG1x, FCARG1x, #sizeof(zend_object)
+		|	OBJ_RELEASE ZREG_FCARG1x, >4, ZREG_TMP1, ZREG_TMP2
+		|4:
+	} else if (may_need_release_this) {
+		if (!left_frame) {
+			left_frame = 1;
+		    if (!zend_jit_leave_frame(Dst)) {
+				return 0;
+		    }
+		}
+		|	// if (call_info & ZEND_CALL_RELEASE_THIS)
+		|	TST_32_WITH_CONST FCARG1w, ZEND_CALL_RELEASE_THIS, TMP1w
+		|	beq >4
+		|	// zend_object *object = Z_OBJ(execute_data->This);
+		|	ldr FCARG1x, EX->This.value.obj
+		|	// OBJ_RELEASE(object);
+		|	OBJ_RELEASE ZREG_FCARG1x, >4, ZREG_TMP1, ZREG_TMP2
+		|4:
+		// TODO: avoid EG(excption) check for $this->foo() calls
+		may_throw = 1;
+	}
+
+	|	// EG(vm_stack_top) = (zval*)execute_data;
+	|	MEM_STORE_ZTS str, FP, executor_globals, vm_stack_top, REG0
+	|	// execute_data = EX(prev_execute_data);
+	|	ldr FP, EX->prev_execute_data
+
+	if (!left_frame) {
+		|	// EG(current_execute_data) = execute_data;
+		|	MEM_STORE_ZTS str, FP, executor_globals, current_execute_data, REG0
+	}
+
+	|9:
+	if (trace) {
+		if (trace->op != ZEND_JIT_TRACE_END
+		 && (JIT_G(current_frame) && !TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
+			zend_jit_reset_last_valid_opline();
+		} else {
+			|	LOAD_IP
+			|	ADD_IP_FROM_CST sizeof(zend_op), TMP1
+		}
+
+		|8:
+
+		if (trace->op == ZEND_JIT_TRACE_BACK
+		 && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
+			const zend_op *next_opline = trace->opline;
+
+			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
+			 && (op1_info & MAY_BE_RC1)
+			 && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
+				/* exception might be thrown during destruction of unused return value */
+				|	// if (EG(exception))
+				|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+				|	cbnz REG0, ->leave_throw_handler
+			}
+			do {
+				trace++;
+			} while (trace->op == ZEND_JIT_TRACE_INIT_CALL);
+			ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
+			next_opline = trace->opline;
+			ZEND_ASSERT(next_opline != NULL);
+
+			if (trace->op == ZEND_JIT_TRACE_END
+			 && trace->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
+				trace_info->flags |= ZEND_JIT_TRACE_LOOP;
+				|	CMP_IP next_opline, TMP1, TMP2
+				|	beq =>0 // LOOP
+#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
+				|	JMP_IP TMP1
+#else
+				|	b ->trace_escape
+#endif
+			} else {
+				|	CMP_IP next_opline, TMP1, TMP2
+				|	bne ->trace_escape
+			}
+
+			zend_jit_set_last_valid_opline(trace->opline);
+
+			return 1;
+		} else if (may_throw ||
+				(((opline->op1_type & (IS_VAR|IS_TMP_VAR))
+				  && (op1_info & MAY_BE_RC1)
+				  && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)))
+				 && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) {
+			|	// if (EG(exception))
+			|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+			|	cbnz REG0, ->leave_throw_handler
+		}
+
+		return 1;
+	} else {
+		|	// if (EG(exception))
+		|	MEM_LOAD_ZTS ldr, REG0, executor_globals, exception, TMP1
+		|	LOAD_IP
+		|	cbnz REG0, ->leave_throw_handler
+		|	// opline = EX(opline) + 1
+		|	ADD_IP_FROM_CST sizeof(zend_op), TMP1
+	}
+
+	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
+		|	ADD_HYBRID_SPAD
+#ifdef CONTEXT_THREADED_JIT
+		|	NIY	// TODO: CONTEXT_THREADED_JIT is always undefined
+#else
+		|	JMP_IP TMP1
+#endif
+	} else if (GCC_GLOBAL_REGS) {
+		|	ldp x29, x30, [sp], # SPAD // stack alignment
+#ifdef CONTEXT_THREADED_JIT
+		|	NIY	// TODO
+#else
+		|	JMP_IP TMP1
+#endif
+	} else {
+#ifdef CONTEXT_THREADED_JIT
+		ZEND_UNREACHABLE();
+		// TODO: context threading can't work without GLOBAL REGS because we have to change
+		//       the value of execute_data in execute_ex()
+		|	NIY	// TODO
+#else
+		|	ldp FP, RX, T2                // retore FP and IP
+		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
+		|	mov RETVALx, #2               // ZEND_VM_LEAVE ????
+		|	ret
+#endif
+	}
+
+	return 1;
+}
+
+static int zend_jit_return(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr)
+{
+	zend_jit_addr ret_addr;
+	int8_t return_value_used;
+
+	ZEND_ASSERT(op_array->type != ZEND_EVAL_CODE && op_array->function_name);
+	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF));
+
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && JIT_G(current_frame)) {
+		if (TRACE_FRAME_IS_RETURN_VALUE_USED(JIT_G(current_frame))) {
+			return_value_used = 1;
+		} else if (TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))) {
+			return_value_used = 0;
+		} else {
+			return_value_used = -1;
+		}
+	} else {
+		return_value_used = -1;
+	}
+
+	if (ZEND_OBSERVER_ENABLED) {
+		if (Z_MODE(op1_addr) == IS_REG) {
+			zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
+
+			if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) {
+				return 0;
+			}
+			op1_addr = dst;
+		}
+		|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
+		|	mov FCARG1x, FP
+		|	SET_EX_OPLINE opline, REG0
+		|	EXT_CALL zend_observer_fcall_end, REG0
+	}
+
+	// if (!EX(return_value))
+	if (Z_MODE(op1_addr) == IS_REG && Z_REG(op1_addr) == ZREG_REG1) {
+		if (return_value_used != 0) {
+			|	ldr REG2, EX->return_value
+		}
+		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, 0);
+	} else {
+		if (return_value_used != 0) {
+			|	ldr REG1, EX->return_value
+		}
+		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG1, 0);
+	}
+	if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+	    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+		if (return_value_used == -1) {
+			|	cbz Rx(Z_REG(ret_addr)), >1
+			|.cold_code
+			|1:
+		}
+		if (return_value_used != 1) {
+			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+				if (jit_return_label >= 0) {
+					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, =>jit_return_label, ZREG_TMP1, ZREG_TMP2
+				} else {
+					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >9, ZREG_TMP1, ZREG_TMP2
+				}
+			}
+			|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+			|	GC_DELREF FCARG1x, TMP1w
+			if (RC_MAY_BE_1(op1_info)) {
+				if (RC_MAY_BE_N(op1_info)) {
+					if (jit_return_label >= 0) {
+						|	bne =>jit_return_label
+					} else {
+						|	bne >9
+					}
+				}
+				|	//SAVE_OPLINE()
+				|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
+				|	//????ldr REG1, EX->return_value // reload ???
+			}
+			if (return_value_used == -1) {
+				if (jit_return_label >= 0) {
+					|	b =>jit_return_label
+				} else {
+					|	b >9
+				}
+				|.code
+			}
+		}
+	} else if (return_value_used == -1) {
+		if (jit_return_label >= 0) {
+			|	cbz Rx(Z_REG(ret_addr)), =>jit_return_label
+		} else {
+			|	cbz Rx(Z_REG(ret_addr)), >9
+		}
+	}
+
+	if (return_value_used == 0) {
+		|9:
+		return 1;
+	}
+
+	if (opline->op1_type == IS_CONST) {
+		zval *zv = RT_CONSTANT(opline, opline->op1);
+		|	ZVAL_COPY_CONST ret_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
+		if (Z_REFCOUNTED_P(zv)) {
+			|	ADDREF_CONST zv, REG0, TMP1
+		}
+	} else if (opline->op1_type == IS_TMP_VAR) {
+		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+	} else if (opline->op1_type == IS_CV) {
+		if (op1_info & MAY_BE_REF) {
+			|	LOAD_ZVAL_ADDR REG0, op1_addr
+			|	ZVAL_DEREF REG0, op1_info, TMP1w
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+		}
+		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+			if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
+			    (op1_info & (MAY_BE_REF|MAY_BE_OBJECT)) ||
+			    !op_array->function_name) {
+				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
+			} else if (return_value_used != 1) {
+				|	// if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) ZVAL_NULL(retval_ptr);
+				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2
+			}
+		}
+	} else {
+		if (op1_info & MAY_BE_REF) {
+			zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, offsetof(zend_reference, val));
+
+			|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1
+			|.cold_code
+			|1:
+			|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
+			|	GET_ZVAL_PTR REG0, op1_addr, TMP1
+			|	// ZVAL_COPY_VALUE(return_value, &ref->value);
+			|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_REG2, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			|	GC_DELREF REG0, TMP1w
+			|	beq >2
+			|	// if (IS_REFCOUNTED())
+			if (jit_return_label >= 0) {
+				|	IF_NOT_REFCOUNTED REG2w, =>jit_return_label, TMP1w
+			} else {
+				|	IF_NOT_REFCOUNTED REG2w, >9, TMP1w
+			}
+			|	// ADDREF
+			|	GET_ZVAL_PTR REG2, ret_addr, TMP1 // reload
+			|	GC_ADDREF REG2, TMP1w
+			if (jit_return_label >= 0) {
+				|	b =>jit_return_label
+			} else {
+				|	b >9
+			}
+			|2:
+			|	mov FCARG1x, REG0
+			|	EFREE_REFERENCE
+			if (jit_return_label >= 0) {
+				|	b =>jit_return_label
+			} else {
+				|	b >9
+			}
+			|.code
+		}
+		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+	}
+
+	|9:
+	return 1;
+}
+
+static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, zend_jit_addr val_addr, zend_reg type_reg)
+{
+	ZEND_ASSERT(type_reg == ZREG_REG2);
+
+	|	GET_ZVAL_PTR REG1, val_addr, TMP1
+	|	IF_NOT_REFCOUNTED REG2w, >2, TMP1w
+	|	GET_LOW_8BITS TMP2w, REG2w
+	|	IF_NOT_TYPE TMP2w, IS_REFERENCE, >1
+	|	add REG1, REG1, #offsetof(zend_reference, val)
+	|	GET_Z_TYPE_INFO REG2w, REG1
+	|	GET_Z_PTR REG1, REG1
+	|	IF_NOT_REFCOUNTED REG2w, >2, TMP1w
+	|1:
+	|	GC_ADDREF REG1, TMP2w
+	|2:
+	|	SET_ZVAL_PTR res_addr, REG1, TMP1
+	|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
+
+	return 1;
+}
+
+static bool zend_jit_may_avoid_refcounting(const zend_op *opline)
+{
+	switch (opline->opcode) {
+		case ZEND_FETCH_OBJ_FUNC_ARG:
+			if (!JIT_G(current_frame) ||
+			    !JIT_G(current_frame)->call->func ||
+			    !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
+				return 0;
+			}
+			/* break missing intentionally */
+		case ZEND_FETCH_OBJ_R:
+		case ZEND_FETCH_OBJ_IS:
+			if (opline->op2_type == IS_CONST
+			 && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING
+			 && Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] != '\0') {
+				return 1;
+			}
+			break;
+		case ZEND_FETCH_DIM_FUNC_ARG:
+			if (!JIT_G(current_frame) ||
+			    !JIT_G(current_frame)->call->func ||
+			    !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
+				return 0;
+			}
+			/* break missing intentionally */
+		case ZEND_FETCH_DIM_R:
+		case ZEND_FETCH_DIM_IS:
+			return 1;
+		case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+			if (!(opline->extended_value & ZEND_ISEMPTY)) {
+				return 1;
+			}
+			break;
+	}
+	return 0;
+}
+
+static int zend_jit_fetch_dim_read(dasm_State        **Dst,
+                                   const zend_op      *opline,
+                                   zend_ssa           *ssa,
+                                   const zend_ssa_op  *ssa_op,
+                                   uint32_t            op1_info,
+                                   zend_jit_addr       op1_addr,
+                                   bool                op1_avoid_refcounting,
+                                   uint32_t            op2_info,
+                                   uint32_t            res_info,
+                                   zend_jit_addr       res_addr,
+                                   int                 may_throw)
+{
+	zend_jit_addr orig_op1_addr, op2_addr;
+	const void *exit_addr = NULL;
+	const void *not_found_exit_addr = NULL;
+	const void *res_exit_addr = NULL;
+	bool result_avoid_refcounting = 0;
+	uint32_t may_be_string = (opline->opcode != ZEND_FETCH_LIST_R) ? MAY_BE_STRING : 0;
+
+	orig_op1_addr = OP1_ADDR();
+	op2_addr = OP2_ADDR();
+
+	if (opline->opcode != ZEND_FETCH_DIM_IS
+	 && JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+		if (!exit_addr) {
+			return 0;
+		}
+	}
+
+	if ((res_info & MAY_BE_GUARD)
+	 && JIT_G(current_frame)
+	 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
+		uint32_t flags = 0;
+		uint32_t old_op1_info = 0;
+		uint32_t old_info;
+		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
+		int32_t exit_point;
+
+		if (opline->opcode != ZEND_FETCH_LIST_R
+		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR))
+		 && !op1_avoid_refcounting) {
+			flags |= ZEND_JIT_EXIT_FREE_OP1;
+		}
+		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
+		 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+			flags |= ZEND_JIT_EXIT_FREE_OP2;
+		}
+		if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
+		 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
+		 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
+		 && (ssa_op+1)->op1_use == ssa_op->result_def
+		 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))
+		 && zend_jit_may_avoid_refcounting(opline+1)) {
+			result_avoid_refcounting = 1;
+			ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
+		}
+
+		if (op1_avoid_refcounting) {
+			old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
+			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
+		}
+
+		if (!(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))) {
+			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
+			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
+			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
+			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
+			res_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+			if (!res_exit_addr) {
+				return 0;
+			}
+			res_info &= ~MAY_BE_GUARD;
+			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
+		}
+
+		if (opline->opcode == ZEND_FETCH_DIM_IS
+		 && !(res_info & MAY_BE_NULL)) {
+			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_NULL, 0);
+			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_NULL);
+			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
+			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
+			not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+			if (!not_found_exit_addr) {
+				return 0;
+			}
+		}
+
+		if (op1_avoid_refcounting) {
+			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
+		}
+	}
+
+	if (op1_info & MAY_BE_REF) {
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	}
+
+	if (op1_info & MAY_BE_ARRAY) {
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
+			if (exit_addr && !(op1_info & (MAY_BE_OBJECT|may_be_string))) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, &exit_addr, ZREG_TMP1
+			} else {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
+			}
+		}
+		|	GET_ZVAL_LVAL ZREG_FCARG1x, op1_addr, TMP1
+		if (!zend_jit_fetch_dimension_address_inner(Dst, opline, (opline->opcode != ZEND_FETCH_DIM_IS) ? BP_VAR_R : BP_VAR_IS, op1_info, op2_info, res_exit_addr, not_found_exit_addr, exit_addr)) {
+			return 0;
+		}
+	}
+
+	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
+		if (op1_info & MAY_BE_ARRAY) {
+			|.cold_code
+			|7:
+		}
+
+		if (opline->opcode != ZEND_FETCH_LIST_R && (op1_info & MAY_BE_STRING)) {
+			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING))) {
+				if (exit_addr && !(op1_info & MAY_BE_OBJECT)) {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &exit_addr, ZREG_TMP1
+				} else {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1
+				}
+			}
+			|	SET_EX_OPLINE opline, REG0
+			|	GET_ZVAL_LVAL ZREG_FCARG1x, op1_addr, TMP1
+			if (opline->opcode != ZEND_FETCH_DIM_IS) {
+				if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) {
+					|	GET_ZVAL_LVAL ZREG_FCARG2x, op2_addr, TMP1
+					|	EXT_CALL zend_jit_fetch_dim_str_offset_r_helper, REG0
+				} else {
+					|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+					|	EXT_CALL zend_jit_fetch_dim_str_r_helper, REG0
+				}
+				|	SET_ZVAL_PTR res_addr, RETVALx, TMP1
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING, TMP1w, TMP2
+			} else {
+				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+				|   LOAD_ZVAL_ADDR CARG3, res_addr
+				|	EXT_CALL zend_jit_fetch_dim_str_is_helper, REG0
+			}
+			if ((op1_info & MAY_BE_ARRAY) ||
+				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING)))) {
+				|	b >9 // END
+			}
+			|6:
+		}
+
+		if (op1_info & MAY_BE_OBJECT) {
+			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string))) {
+				if (exit_addr) {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
+				} else {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >6, ZREG_TMP1
+				}
+			}
+			|	SET_EX_OPLINE opline, REG0
+		    if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		    }
+			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
+				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
+				|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
+			} else {
+				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+			}
+			|   LOAD_ZVAL_ADDR CARG3, res_addr
+			if (opline->opcode != ZEND_FETCH_DIM_IS) {
+				|	EXT_CALL zend_jit_fetch_dim_obj_r_helper, REG0
+			} else {
+				|	EXT_CALL zend_jit_fetch_dim_obj_is_helper, REG0
+			}
+			if ((op1_info & MAY_BE_ARRAY) ||
+				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) {
+				|	b >9 // END
+			}
+			|6:
+		}
+
+		if ((opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) || (op2_info & MAY_BE_UNDEF)) {
+			|	SET_EX_OPLINE opline, REG0
+			if (opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+				|	EXT_CALL zend_jit_undefined_op_helper, REG0
+				|1:
+			}
+
+			if (op2_info & MAY_BE_UNDEF) {
+				|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1
+				|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
+				|	EXT_CALL zend_jit_undefined_op_helper, REG0
+				|1:
+			}
+		}
+
+		if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))
+		 && (!exit_addr || !(op1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_STRING)))) {
+			if (opline->opcode != ZEND_FETCH_DIM_IS && opline->opcode != ZEND_FETCH_LIST_R) {
+				if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
+					|	LOAD_ZVAL_ADDR FCARG1x, orig_op1_addr
+				} else {
+					|	SET_EX_OPLINE opline, REG0
+					if (Z_MODE(op1_addr) != IS_MEM_ZVAL ||
+					    Z_REG(op1_addr) != ZREG_FCARG1x ||
+					    Z_OFFSET(op1_addr) != 0) {
+						|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+					}
+				}
+				|	EXT_CALL zend_jit_invalid_array_access, REG0
+			}
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+			if (op1_info & MAY_BE_ARRAY) {
+				|	b >9 // END
+			}
+		}
+
+		if (op1_info & MAY_BE_ARRAY) {
+			|.code
+		}
+	}
+
+	if (op1_info & MAY_BE_ARRAY) {
+		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+
+		|8:
+		if (res_exit_addr) {
+			uint32_t type = concrete_type(res_info);
+			if (op1_info & MAY_BE_ARRAY_OF_REF) {
+				|	ZVAL_DEREF REG0, MAY_BE_REF, TMP1w
+			}
+			if (type < IS_STRING) {
+				|	IF_NOT_ZVAL_TYPE val_addr, type, &res_exit_addr, ZREG_TMP1
+			} else {
+				|	GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1
+				|	GET_LOW_8BITS TMP1w, REG2w
+				|	IF_NOT_TYPE TMP1w, type, &res_exit_addr
+			}
+			|	// ZVAL_COPY
+			|7:
+			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0
+			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
+				if (type < IS_STRING) {
+					if (Z_REG(res_addr) != ZREG_FP ||
+					    JIT_G(current_frame) == NULL ||
+					    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
+						|	SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2
+					}
+				} else {
+					|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
+					if (!result_avoid_refcounting) {
+						|	TRY_ADDREF res_info, REG2w, REG1, TMP1w
+					}
+				}
+			} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
+				return 0;
+			}
+		} else if (op1_info & MAY_BE_ARRAY_OF_REF) {
+			|	// ZVAL_COPY_DEREF
+			|	GET_ZVAL_TYPE_INFO Rw(ZREG_REG2), val_addr, TMP1
+			if (!zend_jit_zval_copy_deref(Dst, res_addr, val_addr, ZREG_REG2)) {
+				return 0;
+			}
+		} else  {
+			|	// ZVAL_COPY
+			|	ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			|	TRY_ADDREF res_info, REG1w, REG2, TMP1w
+		}
+	}
+	|9: // END
+
+#ifdef ZEND_JIT_USE_RC_INFERENCE
+	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
+		/* Magic offsetGet() may increase refcount of the key */
+		op2_info |= MAY_BE_RCN;
+	}
+#endif
+
+	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting) {
+		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	}
+
+	if (may_throw) {
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_fetch_dim(dasm_State    **Dst,
+                              const zend_op  *opline,
+                              uint32_t        op1_info,
+                              zend_jit_addr   op1_addr,
+                              uint32_t        op2_info,
+                              zend_jit_addr   res_addr,
+                              int             may_throw)
+{
+	zend_jit_addr op2_addr;
+
+	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
+
+	if (op1_info & MAY_BE_REF) {
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w
+		|	GET_Z_PTR FCARG2x, FCARG1x
+		|	ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))]
+		|	cmp TMP1w, #IS_ARRAY
+		|	bne >2
+		|	add FCARG1x, FCARG2x, #offsetof(zend_reference, val)
+		|	b >3
+		|.cold_code
+		|2:
+		|	SET_EX_OPLINE opline, REG0
+		|	EXT_CALL zend_jit_prepare_assign_dim_ref, REG0
+		|	mov FCARG1x, RETVALx
+		|	cbnz FCARG1x, >1
+		|	b ->exception_handler_undef
+		|.code
+		|1:
+		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	}
+
+	if (op1_info & MAY_BE_ARRAY) {
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
+		}
+		|3:
+		|	SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2
+	}
+	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+		if (op1_info & MAY_BE_ARRAY) {
+			|.cold_code
+			|7:
+		}
+		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+			|	CMP_ZVAL_TYPE op1_addr, IS_FALSE, ZREG_TMP1
+			|	bgt >7
+		}
+		if ((op1_info & MAY_BE_UNDEF)
+		 && opline->opcode == ZEND_FETCH_DIM_RW) {
+			if (op1_info & (MAY_BE_NULL|MAY_BE_FALSE)) {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+			}
+			|	SET_EX_OPLINE opline, REG0
+			|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+			|	EXT_CALL zend_jit_undefined_op_helper, REG0
+			|1:
+		}
+		|	// ZVAL_ARR(container, zend_new_array(8));
+		if (Z_REG(op1_addr) != ZREG_FP) {
+			|	str Rx(Z_REG(op1_addr)), T1 // save
+		}
+		|	EXT_CALL _zend_new_array_0, REG0
+		|	mov REG0, RETVALx
+		if (Z_REG(op1_addr) != ZREG_FP) {
+			|	ldr Rx(Z_REG(op1_addr)), T1 // restore
+		}
+		|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
+		|	mov FCARG1x, REG0
+		if (op1_info & MAY_BE_ARRAY) {
+			|	b >1
+			|.code
+			|1:
+		}
+	}
+
+	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+		|6:
+		if (opline->op2_type == IS_UNUSED) {
+			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
+			|	EXT_CALL zend_hash_next_index_insert, REG0
+			|	// if (UNEXPECTED(!var_ptr)) {
+			|	cbz RETVALx, >1
+			|.cold_code
+			|1:
+			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
+			|	CANNOT_ADD_ELEMENT opline
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF, TMP1w, TMP2
+			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
+			|	b >8
+			|.code
+			|	SET_ZVAL_PTR res_addr, RETVALx, TMP1
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2
+		} else {
+			uint32_t type;
+
+			switch (opline->opcode) {
+				case ZEND_FETCH_DIM_W:
+				case ZEND_FETCH_LIST_W:
+					type = BP_VAR_W;
+					break;
+				case ZEND_FETCH_DIM_RW:
+					type = BP_VAR_RW;
+					break;
+				case ZEND_FETCH_DIM_UNSET:
+					type = BP_VAR_UNSET;
+					break;
+				default:
+					ZEND_UNREACHABLE();
+			}
+
+			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, type, op1_info, op2_info, NULL, NULL, NULL)) {
+				return 0;
+			}
+
+			|8:
+			|	SET_ZVAL_PTR res_addr, REG0, TMP1
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2
+
+			if (type == BP_VAR_RW || (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
+				|.cold_code
+				|9:
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+				|	b >8
+				|.code
+			}
+		}
+	}
+
+	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+			|.cold_code
+			|7:
+		}
+
+		|	SET_EX_OPLINE opline, REG0
+		if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		}
+	    if (opline->op2_type == IS_UNUSED) {
+			|	mov FCARG2x, xzr
+		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
+			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
+			|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
+		} else {
+			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+		}
+		|	LOAD_ZVAL_ADDR CARG3, res_addr
+		switch (opline->opcode) {
+			case ZEND_FETCH_DIM_W:
+			case ZEND_FETCH_LIST_W:
+				|	EXT_CALL zend_jit_fetch_dim_obj_w_helper, REG0
+				break;
+			case ZEND_FETCH_DIM_RW:
+				|	EXT_CALL zend_jit_fetch_dim_obj_rw_helper, REG0
+				break;
+//			case ZEND_FETCH_DIM_UNSET:
+//				|	EXT_CALL zend_jit_fetch_dim_obj_unset_helper, REG0
+//				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+
+		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+			|	b >8 // END
+			|.code
+		}
+	}
+
+#ifdef ZEND_JIT_USE_RC_INFERENCE
+	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+		/* ASSIGN_DIM may increase refcount of the key */
+		op2_info |= MAY_BE_RCN;
+	}
+#endif
+
+	|8:
+	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+
+	if (may_throw) {
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int zend_jit_isset_isempty_dim(dasm_State    **Dst,
+                                      const zend_op  *opline,
+                                      uint32_t        op1_info,
+                                      zend_jit_addr   op1_addr,
+                                      bool            op1_avoid_refcounting,
+                                      uint32_t        op2_info,
+                                      int             may_throw,
+                                      zend_uchar      smart_branch_opcode,
+                                      uint32_t        target_label,
+                                      uint32_t        target_label2,
+                                      const void     *exit_addr)
+{
+	zend_jit_addr op2_addr, res_addr;
+
+	// TODO: support for empty() ???
+	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
+
+	op2_addr = OP2_ADDR();
+	res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+	if (op1_info & MAY_BE_REF) {
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	}
+
+	if (op1_info & MAY_BE_ARRAY) {
+		const void *found_exit_addr = NULL;
+		const void *not_found_exit_addr = NULL;
+
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
+		}
+		|	GET_ZVAL_LVAL ZREG_FCARG1x, op1_addr, TMP1
+		if (exit_addr
+		 && !(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY))
+		 && !may_throw
+		 && (!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || op1_avoid_refcounting)
+		 && (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)))) {
+			if (smart_branch_opcode == ZEND_JMPNZ) {
+				found_exit_addr = exit_addr;
+			} else {
+				not_found_exit_addr = exit_addr;
+			}
+		}
+		if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_JIT_IS, op1_info, op2_info, found_exit_addr, not_found_exit_addr, NULL)) {
+			return 0;
+		}
+
+		if (found_exit_addr) {
+			|9:
+			return 1;
+		} else if (not_found_exit_addr) {
+			|8:
+			return 1;
+		}
+	}
+
+	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
+		if (op1_info & MAY_BE_ARRAY) {
+			|.cold_code
+			|7:
+		}
+
+		if (op1_info & (MAY_BE_STRING|MAY_BE_OBJECT)) {
+			|	SET_EX_OPLINE opline, REG0
+		    if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			}
+			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
+				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
+				|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
+			} else {
+				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
+			}
+			|	EXT_CALL zend_jit_isset_dim_helper, REG0
+			|	cbz RETVALw, >9
+			if (op1_info & MAY_BE_ARRAY) {
+				|	b >8
+				|.code
+			}
+		} else {
+			if (op2_info & MAY_BE_UNDEF) {
+				if (op2_info & MAY_BE_ANY) {
+					|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1
+				}
+				|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
+				|	EXT_CALL zend_jit_undefined_op_helper, REG0
+				|1:
+			}
+			if (op1_info & MAY_BE_ARRAY) {
+				|	b >9
+				|.code
+			}
+		}
+	}
+
+#ifdef ZEND_JIT_USE_RC_INFERENCE
+	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
+		/* Magic offsetExists() may increase refcount of the key */
+		op2_info |= MAY_BE_RCN;
+	}
+#endif
+
+	if (op1_info & (MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_OBJECT)) {
+		|8:
+		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		if (!op1_avoid_refcounting) {
+			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		}
+		if (may_throw) {
+			if (!zend_jit_check_exception_undef_result(Dst, opline)) {
+				return 0;
+			}
+		}
+		if (!(opline->extended_value & ZEND_ISEMPTY)) {
+			if (exit_addr) {
+				if (smart_branch_opcode == ZEND_JMPNZ) {
+					|	b &exit_addr
+				} else {
+					|	b >8
+				}
+			} else if (smart_branch_opcode) {
+				if (smart_branch_opcode == ZEND_JMPZ) {
+					|	b =>target_label2
+				} else if (smart_branch_opcode == ZEND_JMPNZ) {
+					|	b =>target_label
+				} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+					|	b =>target_label2
+				} else {
+					ZEND_UNREACHABLE();
+				}
+			} else {
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+				|	b >8
+			}
+		} else {
+			|	NIY // TODO: support for empty()
+		}
+	}
+
+	|9: // not found
+	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	if (!op1_avoid_refcounting) {
+		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	}
+	if (may_throw) {
+		if (!zend_jit_check_exception_undef_result(Dst, opline)) {
+			return 0;
+		}
+	}
+	if (!(opline->extended_value & ZEND_ISEMPTY)) {
+		if (exit_addr) {
+			if (smart_branch_opcode == ZEND_JMPZ) {
+				|	b &exit_addr
+			}
+		} else if (smart_branch_opcode) {
+			if (smart_branch_opcode == ZEND_JMPZ) {
+				|	b =>target_label
+			} else if (smart_branch_opcode == ZEND_JMPNZ) {
+			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+				|	b =>target_label
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		} else {
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+		}
+	} else {
+		|	NIY // TODO: support for empty()
+	}
+
+	|8:
+
+	return 1;
+}
+
+static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
+{
+	zend_jit_addr op1_addr = OP1_ADDR();
+	zend_string *varname = Z_STR_P(RT_CONSTANT(opline, opline->op2));
+
+	|	// idx = (uint32_t)(uintptr_t)CACHED_PTR(opline->extended_value) - 1;
+	|	ldr REG0, EX->run_time_cache
+	|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, opline->extended_value, TMP1
+	|	sub REG0, REG0, #1
+	|	// if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket)))
+	|	MEM_LOAD_32_ZTS ldr, REG1w, executor_globals, symbol_table.nNumUsed, REG1
+	|	cmp REG0, REG1, lsl #5
+	|	bhs >9
+	|	// Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx);
+	|	MEM_LOAD_ZTS ldr, TMP1, executor_globals, symbol_table.arData, REG1
+	|	add REG0, REG0, TMP1
+	|	IF_NOT_Z_TYPE REG0, IS_REFERENCE, >9, TMP1w
+	|	// (EXPECTED(p->key == varname))
+	|	ldr TMP1, [REG0, #offsetof(Bucket, key)]
+	|	LOAD_ADDR TMP2, varname
+	|	cmp TMP1, TMP2
+	|	bne >9
+	|	GET_Z_PTR REG0, REG0
+	|	GC_ADDREF REG0, TMP1w
+	|1:
+	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+			|	// if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr)))
+			|	IF_ZVAL_REFCOUNTED op1_addr, >2, ZREG_TMP1, ZREG_TMP2
+			|.cold_code
+			|2:
+		}
+		|	// zend_refcounted *garbage = Z_COUNTED_P(variable_ptr);
+		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+		|	// ZVAL_REF(variable_ptr, ref)
+		|	SET_ZVAL_PTR op1_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2
+		|	// if (GC_DELREF(garbage) == 0)
+		|	GC_DELREF FCARG1x, TMP1w
+		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
+			|	bne >3
+		} else {
+			|	bne >5
+		}
+		|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
+		|	b >5
+		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
+			|3:
+			|	// GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr)
+			|	IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w
+			|	EXT_CALL gc_possible_root, REG0
+			|	b >5
+		}
+		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+			|.code
+		}
+	}
+
+	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+		|	// ZVAL_REF(variable_ptr, ref)
+		|	SET_ZVAL_PTR op1_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2
+	}
+	|5:
+	//END of handler
+
+	|.cold_code
+	|9:
+	|	LOAD_ADDR FCARG1x, (ptrdiff_t)varname
+	|	ldr FCARG2x, EX->run_time_cache
+	if (opline->extended_value) {
+		|	ADD_SUB_64_WITH_CONST_32 add, FCARG2x, FCARG2x, opline->extended_value, TMP1
+	}
+	|	EXT_CALL zend_jit_fetch_global_helper, REG0
+	|	mov REG0, RETVALx
+	|	b <1
+	|.code
+
+	return 1;
+}
+
+static int zend_jit_verify_arg_type(dasm_State **Dst, const zend_op *opline, zend_arg_info *arg_info, bool check_exception)
+{
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+	bool in_cold = 0;
+	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
+	zend_reg tmp_reg = (type_mask == 0 || is_power_of_two(type_mask)) ? ZREG_FCARG1x : ZREG_REG0;
+
+	if (ZEND_ARG_SEND_MODE(arg_info)) {
+		if (opline->opcode == ZEND_RECV_INIT) {
+			|	LOAD_ZVAL_ADDR Rx(tmp_reg), res_addr
+			|	ZVAL_DEREF Rx(tmp_reg), MAY_BE_REF, TMP1w
+			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, 0);
+		} else {
+			|	GET_ZVAL_PTR Rx(tmp_reg), res_addr, TMP1
+			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, offsetof(zend_reference, val));
+		}
+	}
+
+	if (type_mask != 0) {
+		if (is_power_of_two(type_mask)) {
+			uint32_t type_code = concrete_type(type_mask);
+			|	IF_NOT_ZVAL_TYPE res_addr, type_code, >1, ZREG_TMP1
+		} else {
+			|	mov REG2w, #1
+			|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, REG1w, Rx(Z_REG(res_addr)), Z_OFFSET(res_addr)+offsetof(zval, u1.v.type), TMP1
+			|	lsl REG2w, REG2w, REG1w
+			|	TST_32_WITH_CONST REG2w, type_mask, TMP1w
+			|	beq >1
+		}
+
+		|.cold_code
+		|1:
+
+		in_cold = 1;
+	}
+
+	if (Z_REG(res_addr) != ZREG_FCARG1x || Z_OFFSET(res_addr) != 0) {
+		|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+	}
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+		|	SET_EX_OPLINE opline, REG0
+	} else {
+		|	ADDR_STORE EX->opline, opline, REG0
+	}
+	|	LOAD_ADDR FCARG2x, (ptrdiff_t)arg_info
+	|	EXT_CALL zend_jit_verify_arg_slow, REG0
+
+	if (check_exception) {
+		|	GET_LOW_8BITS REG0w, RETVALw
+		if (in_cold) {
+			|	cbnz REG0w, >1
+			|	b ->exception_handler
+			|.code
+			|1:
+		} else {
+			|	cbz REG0w, ->exception_handler
+		}
+	} else if (in_cold) {
+		|	b >1
+		|.code
+		|1:
+	}
+
+	return 1;
+}
+
+static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array)
+{
+	uint32_t arg_num = opline->op1.num;
+	zend_arg_info *arg_info = NULL;
+
+	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
+		if (EXPECTED(arg_num <= op_array->num_args)) {
+			arg_info = &op_array->arg_info[arg_num-1];
+		} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
+			arg_info = &op_array->arg_info[op_array->num_args];
+		}
+		if (arg_info && !ZEND_TYPE_IS_SET(arg_info->type)) {
+			arg_info = NULL;
+		}
+	}
+
+	if (arg_info || (opline+1)->opcode != ZEND_RECV) {
+		|	ldr TMP1w, EX->This.u2.num_args
+		|	CMP_32_WITH_CONST TMP1w, arg_num, TMP2w
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+			if (!exit_addr) {
+				return 0;
+			}
+			|	blo &exit_addr
+		} else {
+			|	blo >1
+			|.cold_code
+			|1:
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				|	SET_EX_OPLINE opline, REG0
+			} else {
+				|	ADDR_STORE EX->opline, opline, REG0
+			}
+			|	mov FCARG1x, FP
+			|	EXT_CALL zend_missing_arg_error, REG0
+			|	b ->exception_handler
+			|.code
+		}
+	}
+
+	if (arg_info) {
+		if (!zend_jit_verify_arg_type(Dst, opline, arg_info, 1)) {
+			return 0;
+		}
+	}
+
+	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
+		if ((opline+1)->opcode != ZEND_RECV && (opline+1)->opcode != ZEND_RECV_INIT) {
+			|	LOAD_IP_ADDR (opline + 1)
+			zend_jit_set_last_valid_opline(opline + 1);
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool is_last, int may_throw)
+{
+	uint32_t arg_num = opline->op1.num;
+	zval *zv = RT_CONSTANT(opline, opline->op2);
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
+	    (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+		|	ldr TMP1w, EX->This.u2.num_args
+		|	CMP_32_WITH_CONST TMP1w, arg_num, TMP2w
+		|	bhs >5
+	}
+	|	ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
+	if (Z_REFCOUNTED_P(zv)) {
+		|	ADDREF_CONST zv, REG0, TMP1
+	}
+
+	if (Z_CONSTANT_P(zv)) {
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			|	SET_EX_OPLINE opline, REG0
+		} else {
+			|	ADDR_STORE EX->opline, opline, REG0
+		}
+		|	LOAD_ZVAL_ADDR FCARG1x, res_addr
+		|	ldr REG0, EX->func
+		|	ldr FCARG2x, [REG0, #offsetof(zend_op_array, scope)]
+		|	EXT_CALL zval_update_constant_ex, REG0
+		|	cbnz RETVALw, >1
+		|.cold_code
+		|1:
+		|	ZVAL_PTR_DTOR res_addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 1, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF, TMP1w, TMP2
+		|	b ->exception_handler
+		|.code
+	}
+
+	|5:
+
+	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
+		do {
+			zend_arg_info *arg_info;
+
+			if (arg_num <= op_array->num_args) {
+				arg_info = &op_array->arg_info[arg_num-1];
+			} else if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
+				arg_info = &op_array->arg_info[op_array->num_args];
+			} else {
+				break;
+			}
+			if (!ZEND_TYPE_IS_SET(arg_info->type)) {
+				break;
+			}
+			if (!zend_jit_verify_arg_type(Dst, opline, arg_info, may_throw)) {
+				return 0;
+			}
+		} while (0);
+	}
+
+	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
+		if (is_last) {
+			|	LOAD_IP_ADDR (opline + 1)
+			zend_jit_set_last_valid_opline(opline + 1);
+		}
+	}
+	return 1;
+}
+
+static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
+{
+	zend_property_info *info = NULL;
+
+	if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) ||
+	    !ce ||
+	    !(ce->ce_flags & ZEND_ACC_LINKED) ||
+	    (ce->ce_flags & ZEND_ACC_TRAIT) ||
+	    ce->create_object) {
+		return NULL;
+	}
+
+	if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+		if (ce->info.user.filename != filename) {
+			/* class declaration might be changed independently */
+			return NULL;
+		}
+
+		if (ce->parent) {
+			zend_class_entry *parent = ce->parent;
+
+			do {
+				if (parent->type == ZEND_INTERNAL_CLASS) {
+					break;
+				} else if (parent->info.user.filename != filename) {
+					/* some of parents class declarations might be changed independently */
+					/* TODO: this check may be not enough, because even
+					 * in the same it's possible to conditionally define
+					 * few classes with the same name, and "parent" may
+					 * change from request to request.
+					 */
+					return NULL;
+				}
+				parent = parent->parent;
+			} while (parent);
+		}
+	}
+
+	info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
+	if (info == NULL ||
+	    !IS_VALID_PROPERTY_OFFSET(info->offset) ||
+	    (info->flags & ZEND_ACC_STATIC)) {
+		return NULL;
+	}
+
+	if (!(info->flags & ZEND_ACC_PUBLIC) &&
+	    (!on_this || info->ce != ce)) {
+		return NULL;
+	}
+
+	return info;
+}
+
+static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
+{
+	zend_property_info *info;
+
+	if (!ce || (ce->ce_flags & ZEND_ACC_TRAIT)) {
+		return 1;
+	}
+
+	if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+		if (ce->info.user.filename != filename) {
+			/* class declaration might be changed independently */
+			return 1;
+		}
+	}
+
+	info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
+	if (info == NULL ||
+	    !IS_VALID_PROPERTY_OFFSET(info->offset) ||
+	    (info->flags & ZEND_ACC_STATIC)) {
+		return 1;
+	}
+
+	if (!(info->flags & ZEND_ACC_PUBLIC) &&
+	    (!on_this || info->ce != ce)) {
+		return 1;
+	}
+
+	return 0;
+}
+
+static int zend_jit_class_guard(dasm_State **Dst, const zend_op *opline, zend_class_entry *ce)
+{
+	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
+	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+	if (!exit_addr) {
+		return 0;
+	}
+
+	|	LOAD_ADDR TMP1, ((ptrdiff_t)ce)
+	|	ldr TMP2, [FCARG1x, #offsetof(zend_object, ce)]
+	|	cmp TMP2, TMP1
+	|	bne &exit_addr
+
+	return 1;
+}
+
+static int zend_jit_fetch_obj(dasm_State          **Dst,
+                              const zend_op        *opline,
+                              const zend_op_array  *op_array,
+                              zend_ssa             *ssa,
+                              const zend_ssa_op    *ssa_op,
+                              uint32_t              op1_info,
+                              zend_jit_addr         op1_addr,
+                              bool                  op1_indirect,
+                              zend_class_entry     *ce,
+                              bool                  ce_is_instanceof,
+                              bool                  use_this,
+                              bool                  op1_avoid_refcounting,
+                              zend_class_entry     *trace_ce,
+                              int                   may_throw)
+{
+	zval *member;
+	zend_property_info *prop_info;
+	bool may_be_dynamic = 1;
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+	zend_jit_addr prop_addr;
+	uint32_t res_info = RES_INFO();
+
+	ZEND_ASSERT(opline->op2_type == IS_CONST);
+	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
+
+	member = RT_CONSTANT(opline, opline->op2);
+	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
+	prop_info = zend_get_known_property_info(op_array, ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
+
+	if (opline->op1_type == IS_UNUSED || use_this) {
+		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
+	} else {
+		if (opline->op1_type == IS_VAR
+		 && opline->opcode == ZEND_FETCH_OBJ_W
+		 && (op1_info & MAY_BE_INDIRECT)
+		 && Z_REG(op1_addr) == ZREG_FP) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
+			|	GET_Z_PTR FCARG1x, FCARG1x
+			|1:
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & MAY_BE_REF) {
+			if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			}
+			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
+			} else {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >7, ZREG_TMP1
+			}
+		}
+		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+	}
+
+	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+		prop_info = zend_get_known_property_info(op_array, trace_ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
+		if (prop_info) {
+			ce = trace_ce;
+			ce_is_instanceof = 0;
+			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
+				if (!zend_jit_class_guard(Dst, opline, trace_ce)) {
+					return 0;
+				}
+				if (ssa->var_info && ssa_op->op1_use >= 0) {
+					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
+					ssa->var_info[ssa_op->op1_use].ce = ce;
+					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
+				}
+			}
+		}
+	}
+
+	if (!prop_info) {
+		|	ldr REG0, EX->run_time_cache
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG2, REG0, (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS), TMP1
+		|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
+		|	cmp REG2, TMP1
+		|	bne >5
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, ((opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS)  + sizeof(void*)), TMP1
+		may_be_dynamic = zend_may_be_dynamic_property(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
+		if (may_be_dynamic) {
+			|	tst REG0, REG0
+			if (opline->opcode == ZEND_FETCH_OBJ_W) {
+				|	blt >5
+			} else {
+				|	blt >8 // dynamic property
+			}
+		}
+		|	add TMP1, FCARG1x, REG0
+		|	ldr REG2w, [TMP1, #offsetof(zval,u1.type_info)]
+		|	IF_UNDEF REG2w, >5
+		|	mov FCARG1x, TMP1
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		if (opline->opcode == ZEND_FETCH_OBJ_W
+		 && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS)
+		 && (!ce ||	ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS))) {
+			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
+
+			|	ldr REG0, EX->run_time_cache
+			|	SAFE_MEM_ACC_WITH_UOFFSET ldr, FCARG2x, REG0, ((opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS)  + sizeof(void*) * 2), TMP1
+			|	cbnz FCARG2x, >1
+			|.cold_code
+			|1:
+			if (flags == ZEND_FETCH_DIM_WRITE) {
+				|	SET_EX_OPLINE opline, REG0
+				|	EXT_CALL zend_jit_check_array_promotion, REG0
+				|	b >9
+			} else if (flags == ZEND_FETCH_REF) {
+				|	LOAD_ZVAL_ADDR CARG3, res_addr
+				|	EXT_CALL zend_jit_create_typed_ref, REG0
+				|	b >9
+			} else {
+				ZEND_UNREACHABLE();
+			}
+			|.code
+		}
+	} else {
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, prop_info->offset);
+		|	SAFE_MEM_ACC_WITH_UOFFSET_32 ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			if (opline->opcode == ZEND_FETCH_OBJ_W || !(res_info & MAY_BE_GUARD) || !JIT_G(current_frame)) {
+				/* perform IS_UNDEF check only after result type guard (during deoptimization) */
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+				|	IF_UNDEF REG2w, &exit_addr
+			}
+		} else {
+			|	IF_UNDEF REG2w, >5
+		}
+		if (opline->opcode == ZEND_FETCH_OBJ_W
+		 && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS)
+		 && ZEND_TYPE_IS_SET(prop_info->type)) {
+			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
+
+			if (flags == ZEND_FETCH_DIM_WRITE) {
+				if ((ZEND_TYPE_FULL_MASK(prop_info->type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) == 0) {
+					|	cmp REG2w, #IS_FALSE
+					|	ble >1
+					|.cold_code
+					|1:
+					if (Z_REG(prop_addr) != ZREG_FCARG1x || Z_OFFSET(prop_addr) != 0) {
+						|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
+					}
+					|	LOAD_ADDR FCARG2x, prop_info
+					|	SET_EX_OPLINE opline, REG0
+					|	EXT_CALL zend_jit_check_array_promotion, REG0
+					|	b >9
+					|.code
+				}
+			} else if (flags == ZEND_FETCH_REF) {
+				|	GET_LOW_8BITS TMP1w, REG2w
+				|	IF_TYPE TMP1w, IS_REFERENCE, >1
+				if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+					|	LOAD_ADDR FCARG2x, prop_info
+				} else {
+					int prop_info_offset =
+						(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
+
+					|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
+					|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
+					|	SAFE_MEM_ACC_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
+				}
+				if (Z_REG(prop_addr) != ZREG_FCARG1x || Z_OFFSET(prop_addr) != 0) {
+					|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
+				}
+				|	LOAD_ZVAL_ADDR CARG3, res_addr
+				|	EXT_CALL zend_jit_create_typed_ref, REG0
+				|	b >9
+				|1:
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		}
+	}
+	if (op1_avoid_refcounting) {
+		SET_STACK_REG(JIT_G(current_frame)->stack,
+			EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
+	}
+	if (opline->opcode == ZEND_FETCH_OBJ_W) {
+		if (Z_REG(prop_addr) != ZREG_FCARG1x || Z_OFFSET(prop_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
+		}
+		|	SET_ZVAL_PTR res_addr, FCARG1x, TMP1
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && prop_info) {
+			ssa->var_info[ssa_op->result_def].indirect_reference = 1;
+		}
+	} else {
+		bool result_avoid_refcounting = 0;
+
+		if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) {
+			uint32_t flags = 0;
+			uint32_t old_info;
+			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
+			int32_t exit_point;
+			const void *exit_addr;
+			uint32_t type;
+			zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+
+			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
+			 && !use_this
+			 && !op1_avoid_refcounting) {
+				flags = ZEND_JIT_EXIT_FREE_OP1;
+			}
+
+			|	LOAD_ZVAL_ADDR REG0, prop_addr
+
+			if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
+			 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
+			 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
+			 && (ssa_op+1)->op1_use == ssa_op->result_def
+			 && zend_jit_may_avoid_refcounting(opline+1)) {
+				result_avoid_refcounting = 1;
+				ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
+			}
+
+			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
+			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
+			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
+			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
+			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+			if (!exit_addr) {
+				return 0;
+			}
+
+			res_info &= ~MAY_BE_GUARD;
+			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
+			type = concrete_type(res_info);
+
+			|	// ZVAL_DEREF()
+			|	GET_LOW_8BITS TMP1w, REG2w
+			|	IF_NOT_TYPE TMP1w, IS_REFERENCE, >1
+			|	GET_Z_PTR REG0, REG0
+			|	add REG0, REG0, #offsetof(zend_reference, val)
+			if (type < IS_STRING) {
+				|1:
+				|	IF_NOT_ZVAL_TYPE val_addr, type, &exit_addr, ZREG_TMP1
+			} else {
+				|	GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1
+				|1:
+				|	GET_LOW_8BITS TMP1w, REG2w
+				|	IF_NOT_TYPE TMP1w, type, &exit_addr
+			}
+			|	// ZVAL_COPY
+			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0
+			if (type < IS_STRING) {
+				if (Z_REG(res_addr) != ZREG_FP ||
+				    JIT_G(current_frame) == NULL ||
+				    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
+					|	SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2
+				}
+			} else {
+				|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
+				if (!result_avoid_refcounting) {
+					|	TRY_ADDREF res_info, REG2w, REG1, TMP1w
+				}
+			}
+		} else {
+			if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_REG2)) {
+				return 0;
+			}
+		}
+	}
+
+	|.cold_code
+
+	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || !prop_info) {
+		|5:
+		|	SET_EX_OPLINE opline, REG0
+		if (opline->opcode == ZEND_FETCH_OBJ_W) {
+			|	EXT_CALL zend_jit_fetch_obj_w_slow, REG0
+		} else if (opline->opcode != ZEND_FETCH_OBJ_IS) {
+			|	EXT_CALL zend_jit_fetch_obj_r_slow, REG0
+		} else {
+			|	EXT_CALL zend_jit_fetch_obj_is_slow, REG0
+		}
+		|	b >9
+	}
+
+	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)- MAY_BE_OBJECT)) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
+		|7:
+		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
+			|	SET_EX_OPLINE opline, REG0
+			if (opline->opcode != ZEND_FETCH_OBJ_W
+			 && (op1_info & MAY_BE_UNDEF)) {
+				zend_jit_addr orig_op1_addr = OP1_ADDR();
+
+				if (op1_info & MAY_BE_ANY) {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
+				}
+				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+				|	EXT_CALL zend_jit_undefined_op_helper, REG0
+				|1:
+				|	LOAD_ZVAL_ADDR FCARG1x, orig_op1_addr
+			} else if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			}
+			|	LOAD_ADDR FCARG2x, Z_STRVAL_P(member)
+			if (opline->opcode == ZEND_FETCH_OBJ_W) {
+				|	EXT_CALL zend_jit_invalid_property_write, REG0
+				|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR, TMP1w, TMP2
+			} else {
+				|	EXT_CALL zend_jit_invalid_property_read, REG0
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+			}
+			|	b >9
+		} else {
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+			|	b >9
+		}
+	}
+
+	if (!prop_info
+	 && may_be_dynamic
+	 && opline->opcode != ZEND_FETCH_OBJ_W) {
+		|8:
+		|	mov FCARG2x, REG0
+		|	SET_EX_OPLINE opline, REG0
+		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
+			|	EXT_CALL zend_jit_fetch_obj_r_dynamic, REG0
+		} else {
+			|	EXT_CALL zend_jit_fetch_obj_is_dynamic, REG0
+		}
+		|	b >9
+	}
+
+	|.code;
+	|9: // END
+	if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) {
+		if (opline->op1_type == IS_VAR
+		 && opline->opcode == ZEND_FETCH_OBJ_W
+		 && (op1_info & MAY_BE_RC1)) {
+			zend_jit_addr orig_op1_addr = OP1_ADDR();
+
+			|	IF_NOT_ZVAL_REFCOUNTED orig_op1_addr, >1, ZREG_TMP1, ZREG_TMP2
+			|	GET_ZVAL_PTR FCARG1x, orig_op1_addr, TMP1
+			|	GC_DELREF FCARG1x, TMP1w
+			|	bne >1
+			|	SET_EX_OPLINE opline, REG0
+			|	EXT_CALL zend_jit_extract_helper, REG0
+			|1:
+		} else if (!op1_avoid_refcounting) {
+			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+		}
+	}
+
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
+	 && prop_info
+	 && opline->op1_type != IS_VAR
+	 && opline->op1_type != IS_TMP_VAR) {
+		may_throw = 0;
+	}
+
+	if (may_throw) {
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_incdec_obj(dasm_State          **Dst,
+                               const zend_op        *opline,
+                               const zend_op_array  *op_array,
+                               zend_ssa             *ssa,
+                               const zend_ssa_op    *ssa_op,
+                               uint32_t              op1_info,
+                               zend_jit_addr         op1_addr,
+                               bool                  op1_indirect,
+                               zend_class_entry     *ce,
+                               bool                  ce_is_instanceof,
+                               bool                  use_this,
+                               zend_class_entry     *trace_ce,
+                               int                   may_throw)
+{
+	zval *member;
+	zend_string *name;
+	zend_property_info *prop_info;
+	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+	zend_jit_addr res_addr = 0;
+	zend_jit_addr prop_addr;
+	bool needs_slow_path = 0;
+
+	ZEND_ASSERT(opline->op2_type == IS_CONST);
+	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
+
+	if (opline->result_type != IS_UNUSED) {
+		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+	}
+
+	member = RT_CONSTANT(opline, opline->op2);
+	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
+	name = Z_STR_P(member);
+	prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+
+	if (opline->op1_type == IS_UNUSED || use_this) {
+		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
+	} else {
+		if (opline->op1_type == IS_VAR
+		 && (op1_info & MAY_BE_INDIRECT)
+		 && Z_REG(op1_addr) == ZREG_FP) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
+			|	GET_Z_PTR FCARG1x, FCARG1x
+			|1:
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & MAY_BE_REF) {
+			if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			}
+			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
+			} else {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
+				|.cold_code
+				|1:
+				|	SET_EX_OPLINE opline, REG0
+				if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+				}
+				|	LOAD_ADDR FCARG2x, ZSTR_VAL(name)
+				|	EXT_CALL zend_jit_invalid_property_incdec, REG0
+				|	b ->exception_handler
+				|.code
+			}
+		}
+		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+	}
+
+	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+		prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+		if (prop_info) {
+			ce = trace_ce;
+			ce_is_instanceof = 0;
+			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
+				if (!zend_jit_class_guard(Dst, opline, trace_ce)) {
+					return 0;
+				}
+				if (ssa->var_info && ssa_op->op1_use >= 0) {
+					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
+					ssa->var_info[ssa_op->op1_use].ce = ce;
+					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
+				}
+				if (ssa->var_info && ssa_op->op1_def >= 0) {
+					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
+					ssa->var_info[ssa_op->op1_def].ce = ce;
+					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
+				}
+			}
+		}
+	}
+
+	if (!prop_info) {
+		needs_slow_path = 1;
+
+		|	ldr REG0, EX->run_time_cache
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG2, REG0, opline->extended_value, TMP1
+		|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
+		|	cmp REG2, TMP1
+		|	bne >7
+		if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+			|	SAFE_MEM_ACC_WITH_UOFFSET ldr, TMP1, REG0, (opline->extended_value + sizeof(void*) * 2), TMP1
+			|	cbnz TMP1, >7
+		}
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, (opline->extended_value + sizeof(void*)), TMP1
+		|	tst REG0, REG0
+		|	blt >7
+		|	add TMP1, FCARG1x, REG0
+		|	ldrb TMP2w, [TMP1, #offsetof(zval,u1.v.type)]
+		|	IF_TYPE TMP2w , IS_UNDEF, >7
+		|	mov FCARG1x, TMP1
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	} else {
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, prop_info->offset);
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+			if (!exit_addr) {
+				return 0;
+			}
+			|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
+			|	IF_TYPE TMP2w, IS_UNDEF, &exit_addr
+		} else {
+			|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
+			|	IF_TYPE TMP2w, IS_UNDEF, >7
+			needs_slow_path = 1;
+		}
+		if (ZEND_TYPE_IS_SET(prop_info->type)) {
+			|	SET_EX_OPLINE opline, REG0
+			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+				|	LOAD_ADDR FCARG2x, prop_info
+			} else {
+				int prop_info_offset =
+					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
+
+				|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
+				|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
+				|	SAFE_MEM_ACC_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
+			}
+			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
+			if (opline->result_type == IS_UNUSED) {
+				switch (opline->opcode) {
+					case ZEND_PRE_INC_OBJ:
+					case ZEND_POST_INC_OBJ:
+						|	EXT_CALL zend_jit_inc_typed_prop, REG0
+						break;
+					case ZEND_PRE_DEC_OBJ:
+					case ZEND_POST_DEC_OBJ:
+						|	EXT_CALL zend_jit_dec_typed_prop, REG0
+						break;
+					default:
+						ZEND_UNREACHABLE();
+				}
+			} else {
+				|	LOAD_ZVAL_ADDR CARG3, res_addr
+				switch (opline->opcode) {
+					case ZEND_PRE_INC_OBJ:
+						|	EXT_CALL zend_jit_pre_inc_typed_prop, REG0
+						break;
+					case ZEND_PRE_DEC_OBJ:
+						|	EXT_CALL zend_jit_pre_dec_typed_prop, REG0
+						break;
+					case ZEND_POST_INC_OBJ:
+						|	EXT_CALL zend_jit_post_inc_typed_prop, REG0
+						break;
+					case ZEND_POST_DEC_OBJ:
+						|	EXT_CALL zend_jit_post_dec_typed_prop, REG0
+						break;
+					default:
+						ZEND_UNREACHABLE();
+				}
+			}
+		}
+	}
+
+	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
+		zend_jit_addr var_addr = prop_addr;
+
+		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		if (Z_REG(prop_addr) != ZREG_FCARG1x || Z_OFFSET(prop_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
+		}
+
+		|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2, ZREG_TMP1
+		|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
+		|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
+		|	cbnz TMP1, >1
+		|	add FCARG1x, FCARG1x, #offsetof(zend_reference, val)
+		|.cold_code
+		|1:
+		if (opline) {
+			|	SET_EX_OPLINE opline, REG0
+		}
+		if (opline->result_type == IS_UNUSED) {
+			|	mov FCARG2x, xzr
+		} else {
+			|	LOAD_ZVAL_ADDR FCARG2x, res_addr
+		}
+		switch (opline->opcode) {
+			case ZEND_PRE_INC_OBJ:
+				|	EXT_CALL zend_jit_pre_inc_typed_ref, REG0
+				break;
+			case ZEND_PRE_DEC_OBJ:
+				|	EXT_CALL zend_jit_pre_dec_typed_ref, REG0
+				break;
+			case ZEND_POST_INC_OBJ:
+				|	EXT_CALL zend_jit_post_inc_typed_ref, REG0
+				break;
+			case ZEND_POST_DEC_OBJ:
+				|	EXT_CALL zend_jit_post_dec_typed_ref, REG0
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+		|	b >9
+		|.code
+
+		|2:
+		|	IF_NOT_ZVAL_TYPE var_addr, IS_LONG, >2, ZREG_TMP1
+		if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
+			if (opline->result_type != IS_UNUSED) {
+				|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			}
+		}
+		if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
+			|	LONG_ADD_SUB_WITH_IMM adds, var_addr, Z_L(1), TMP1, TMP2
+		} else {
+			|	LONG_ADD_SUB_WITH_IMM subs, var_addr, Z_L(1), TMP1, TMP2
+		}
+		|	bvs	>3
+		if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) {
+			if (opline->result_type != IS_UNUSED) {
+				|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			}
+		}
+		|.cold_code
+		|2:
+		if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
+			|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_ANY, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			|	TRY_ADDREF MAY_BE_ANY, REG0w, REG2, TMP1w
+		}
+		if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
+			if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
+				|	LOAD_ZVAL_ADDR FCARG2x, res_addr
+				|	EXT_CALL zend_jit_pre_inc, REG0
+			} else {
+				|	EXT_CALL increment_function, REG0
+			}
+		} else {
+			if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
+				|	LOAD_ZVAL_ADDR FCARG2x, res_addr
+				|	EXT_CALL zend_jit_pre_dec, REG0
+			} else {
+				|	EXT_CALL decrement_function, REG0
+			}
+		}
+		|	b >4
+
+		|3:
+		if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
+			uint64_t val = 0x43e0000000000000;
+			|	LOAD_64BIT_VAL REG0, val
+			|	SET_ZVAL_LVAL_FROM_REG var_addr, REG0, TMP1
+			if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
+				|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
+			}
+		} else {
+			uint64_t val = 0xc3e0000000000000;
+			|	LOAD_64BIT_VAL REG0, val
+			|	SET_ZVAL_LVAL_FROM_REG var_addr, REG0, TMP1
+			if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
+				|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
+			}
+		}
+		|	b >4
+		|.code
+		|4:
+	}
+
+	if (needs_slow_path) {
+		|.cold_code
+		|7:
+		|	SET_EX_OPLINE opline, REG0
+		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
+		|	LOAD_ADDR FCARG2x, name
+		|	ldr CARG3, EX->run_time_cache
+		|	ADD_SUB_64_WITH_CONST_32 add, CARG3, CARG3, opline->extended_value, TMP1
+		if (opline->result_type == IS_UNUSED) {
+			|	mov CARG4, xzr
+		} else {
+			|	LOAD_ZVAL_ADDR CARG4, res_addr
+		}
+
+		switch (opline->opcode) {
+			case ZEND_PRE_INC_OBJ:
+				|	EXT_CALL zend_jit_pre_inc_obj_helper, REG0
+				break;
+			case ZEND_PRE_DEC_OBJ:
+				|	EXT_CALL zend_jit_pre_dec_obj_helper, REG0
+				break;
+			case ZEND_POST_INC_OBJ:
+				|	EXT_CALL zend_jit_post_inc_obj_helper, REG0
+				break;
+			case ZEND_POST_DEC_OBJ:
+				|	EXT_CALL zend_jit_post_dec_obj_helper, REG0
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+
+		|	b >9
+		|.code
+	}
+
+	|9:
+	if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) {
+		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+	}
+
+	if (may_throw) {
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_assign_obj_op(dasm_State          **Dst,
+                                  const zend_op        *opline,
+                                  const zend_op_array  *op_array,
+                                  zend_ssa             *ssa,
+                                  const zend_ssa_op    *ssa_op,
+                                  uint32_t              op1_info,
+                                  zend_jit_addr         op1_addr,
+                                  uint32_t              val_info,
+                                  zend_ssa_range       *val_range,
+                                  bool                  op1_indirect,
+                                  zend_class_entry     *ce,
+                                  bool                  ce_is_instanceof,
+                                  bool                  use_this,
+                                  zend_class_entry     *trace_ce,
+                                  int                   may_throw)
+{
+	zval *member;
+	zend_string *name;
+	zend_property_info *prop_info;
+	zend_jit_addr val_addr = OP1_DATA_ADDR();
+	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+	zend_jit_addr prop_addr;
+	bool needs_slow_path = 0;
+	binary_op_type binary_op = get_binary_op(opline->extended_value);
+
+	ZEND_ASSERT(opline->op2_type == IS_CONST);
+	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
+	ZEND_ASSERT(opline->result_type == IS_UNUSED);
+
+	member = RT_CONSTANT(opline, opline->op2);
+	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
+	name = Z_STR_P(member);
+	prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+
+	if (opline->op1_type == IS_UNUSED || use_this) {
+		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
+	} else {
+		if (opline->op1_type == IS_VAR
+		 && (op1_info & MAY_BE_INDIRECT)
+		 && Z_REG(op1_addr) == ZREG_FP) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
+			|	GET_Z_PTR FCARG1x, FCARG1x
+			|1:
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & MAY_BE_REF) {
+			if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			}
+			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
+			} else {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
+				|.cold_code
+				|1:
+				|	SET_EX_OPLINE opline, REG0
+				if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+				}
+				|	LOAD_ADDR FCARG2x, ZSTR_VAL(name)
+				if (op1_info & MAY_BE_UNDEF) {
+					|	EXT_CALL zend_jit_invalid_property_assign_op, REG0
+				} else {
+					|	EXT_CALL zend_jit_invalid_property_assign, REG0
+				}
+				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
+				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+					|	b >8
+				} else {
+					|	b ->exception_handler
+				}
+				|.code
+			}
+		}
+		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+	}
+
+	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+		prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+		if (prop_info) {
+			ce = trace_ce;
+			ce_is_instanceof = 0;
+			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
+				if (!zend_jit_class_guard(Dst, opline, trace_ce)) {
+					return 0;
+				}
+				if (ssa->var_info && ssa_op->op1_use >= 0) {
+					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
+					ssa->var_info[ssa_op->op1_use].ce = ce;
+					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
+				}
+				if (ssa->var_info && ssa_op->op1_def >= 0) {
+					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
+					ssa->var_info[ssa_op->op1_def].ce = ce;
+					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
+				}
+			}
+		}
+	}
+
+	if (!prop_info) {
+		needs_slow_path = 1;
+
+		|	ldr REG0, EX->run_time_cache
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG2, REG0, ((opline+1)->extended_value), TMP1
+		|	ldr TMP2, [FCARG1x, #offsetof(zend_object, ce)]
+		|	cmp REG2, TMP2
+		|	bne >7
+		if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+			|	SAFE_MEM_ACC_WITH_UOFFSET ldr, TMP1, REG0, ((opline+1)->extended_value + sizeof(void*) * 2), TMP1
+			|	cbnz TMP1, >7
+		}
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, ((opline+1)->extended_value + sizeof(void*)), TMP1
+		|	tst REG0, REG0
+		|	blt >7
+		|	add TMP1, FCARG1x, REG0
+		|	ldrb TMP2w, [TMP1, #offsetof(zval,u1.v.type)]
+		|	IF_TYPE TMP2w, IS_UNDEF, >7
+		|	mov FCARG1x, TMP1
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	} else {
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, prop_info->offset);
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+			if (!exit_addr) {
+				return 0;
+			}
+			|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
+			|	IF_TYPE TMP2w, IS_UNDEF, &exit_addr
+		} else {
+			|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
+			|	IF_TYPE TMP2w, IS_UNDEF, >7
+			needs_slow_path = 1;
+		}
+		if (ZEND_TYPE_IS_SET(prop_info->type)) {
+			uint32_t info = val_info;
+
+			if (opline) {
+				|	SET_EX_OPLINE opline, REG0
+			}
+
+			|	IF_ZVAL_TYPE prop_addr, IS_REFERENCE, >1, ZREG_TMP1
+			|.cold_code
+			|1:
+			|	GET_ZVAL_PTR FCARG1x, prop_addr, TMP1
+			if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2x || Z_OFFSET(val_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG2x, val_addr
+			}
+			|	LOAD_ADDR CARG3, binary_op
+			|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
+			|	b >9
+			|.code
+
+			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
+
+			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+				|	LOAD_ADDR FCARG2x, prop_info
+			} else {
+				int prop_info_offset =
+					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
+
+				|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
+				|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
+				|	SAFE_MEM_ACC_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
+			}
+			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
+			|	LOAD_ZVAL_ADDR CARG3, val_addr
+			|	LOAD_ADDR CARG4, binary_op
+
+			|	EXT_CALL zend_jit_assign_op_to_typed_prop, REG0
+
+			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+				info |= MAY_BE_RC1|MAY_BE_RCN;
+			}
+
+			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		}
+	}
+
+	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
+		zend_jit_addr var_addr = prop_addr;
+		uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
+		uint32_t var_def_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
+
+		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+		|	LOAD_ZVAL_ADDR REG0, prop_addr
+
+		|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2, ZREG_TMP1
+		|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
+		|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
+		|	cbnz TMP1, >1
+		|	add REG0, FCARG1x, #offsetof(zend_reference, val)
+		|.cold_code
+		|1:
+		if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2x || Z_OFFSET(val_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG2x, val_addr
+		}
+		if (opline) {
+			|	SET_EX_OPLINE opline, REG0
+		}
+		|	LOAD_ADDR CARG3, binary_op
+		|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
+		|	b >9
+		|.code
+		|2:
+
+		switch (opline->extended_value) {
+			case ZEND_ADD:
+			case ZEND_SUB:
+			case ZEND_MUL:
+			case ZEND_DIV:
+				if (!zend_jit_math_helper(Dst, opline, opline->extended_value, IS_CV, opline->op1, var_addr, var_info, (opline+1)->op1_type, (opline+1)->op1, val_addr, val_info, 0, var_addr, var_def_info, var_info,
+						1 /* may overflow */, 0)) {
+					return 0;
+				}
+				break;
+			case ZEND_BW_OR:
+			case ZEND_BW_AND:
+			case ZEND_BW_XOR:
+			case ZEND_SL:
+			case ZEND_SR:
+			case ZEND_MOD:
+				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
+						IS_CV, opline->op1, var_addr, var_info, NULL,
+						(opline+1)->op1_type, (opline+1)->op1, val_addr, val_info,
+						val_range,
+						0, var_addr, var_def_info, var_info, 0)) {
+					return 0;
+				}
+				break;
+			case ZEND_CONCAT:
+				if (!zend_jit_concat_helper(Dst, opline, IS_CV, opline->op1, var_addr, var_info, (opline+1)->op1_type, (opline+1)->op1, val_addr, val_info, var_addr,
+						0)) {
+					return 0;
+				}
+				break;
+			default:
+				ZEND_UNREACHABLE();
+		}
+	}
+
+	if (needs_slow_path) {
+		|.cold_code
+		|7:
+		|	SET_EX_OPLINE opline, REG0
+		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
+		|	LOAD_ADDR FCARG2x, name
+		|	LOAD_ZVAL_ADDR CARG3, val_addr
+		|	ldr CARG4, EX->run_time_cache
+		|	ADD_SUB_64_WITH_CONST_32 add, CARG4, CARG4, (opline+1)->extended_value, TMP1
+		|	LOAD_ADDR CARG5, binary_op
+		|	EXT_CALL zend_jit_assign_obj_op_helper, REG0
+
+		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+			val_info |= MAY_BE_RC1|MAY_BE_RCN;
+		}
+
+		|8:
+		|	// FREE_OP_DATA();
+		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|	b >9
+		|.code
+	}
+
+	|9:
+	if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) {
+		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+	}
+
+	if (may_throw) {
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_assign_obj(dasm_State          **Dst,
+                               const zend_op        *opline,
+                               const zend_op_array  *op_array,
+                               zend_ssa             *ssa,
+                               const zend_ssa_op    *ssa_op,
+                               uint32_t              op1_info,
+                               zend_jit_addr         op1_addr,
+                               uint32_t              val_info,
+                               bool                  op1_indirect,
+                               zend_class_entry     *ce,
+                               bool                  ce_is_instanceof,
+                               bool                  use_this,
+                               zend_class_entry     *trace_ce,
+                               int                   may_throw)
+{
+	zval *member;
+	zend_string *name;
+	zend_property_info *prop_info;
+	zend_jit_addr val_addr = OP1_DATA_ADDR();
+	zend_jit_addr res_addr = 0;
+	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
+	zend_jit_addr prop_addr;
+	bool needs_slow_path = 0;
+
+	if (RETURN_VALUE_USED(opline)) {
+		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+	}
+
+	ZEND_ASSERT(opline->op2_type == IS_CONST);
+	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
+
+	member = RT_CONSTANT(opline, opline->op2);
+	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
+	name = Z_STR_P(member);
+	prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+
+	if (opline->op1_type == IS_UNUSED || use_this) {
+		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
+	} else {
+		if (opline->op1_type == IS_VAR
+		 && (op1_info & MAY_BE_INDIRECT)
+		 && Z_REG(op1_addr) == ZREG_FP) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
+			|	GET_Z_PTR FCARG1x, FCARG1x
+			|1:
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & MAY_BE_REF) {
+			if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			}
+			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
+			} else {
+				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
+				|.cold_code
+				|1:
+				|	SET_EX_OPLINE opline, REG0
+				if (Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+				}
+				|	LOAD_ADDR FCARG2x, ZSTR_VAL(name)
+				|	EXT_CALL zend_jit_invalid_property_assign, REG0
+				if (RETURN_VALUE_USED(opline)) {
+					|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
+				}
+				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
+				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+					|	b >8
+				} else {
+					|	b ->exception_handler
+				}
+				|.code
+			}
+		}
+		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+	}
+
+	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
+		prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename);
+		if (prop_info) {
+			ce = trace_ce;
+			ce_is_instanceof = 0;
+			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
+				if (!zend_jit_class_guard(Dst, opline, trace_ce)) {
+					return 0;
+				}
+				if (ssa->var_info && ssa_op->op1_use >= 0) {
+					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
+					ssa->var_info[ssa_op->op1_use].ce = ce;
+					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
+				}
+				if (ssa->var_info && ssa_op->op1_def >= 0) {
+					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
+					ssa->var_info[ssa_op->op1_def].ce = ce;
+					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
+				}
+			}
+		}
+	}
+
+	if (!prop_info) {
+		needs_slow_path = 1;
+
+		|	ldr REG0, EX->run_time_cache
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG2, REG0, opline->extended_value, TMP1
+		|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
+		|	cmp REG2, TMP1
+		|	bne >5
+		if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+			|	SAFE_MEM_ACC_WITH_UOFFSET ldr, FCARG2x, REG0, (opline->extended_value + sizeof(void*) * 2), TMP1
+		}
+		|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, REG0, (opline->extended_value + sizeof(void*)), TMP1
+		|	tst REG0, REG0
+		|	blt >5
+		|	add TMP2, FCARG1x, REG0
+		|	ldrb TMP1w, [TMP2, #offsetof(zval,u1.v.type)]
+		|	IF_TYPE TMP1w, IS_UNDEF, >5
+		|	mov FCARG1x, TMP2
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
+			|	cbnz FCARG2x, >1
+			|.cold_code
+			|1:
+			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
+			|	SET_EX_OPLINE opline, REG0
+			|	LOAD_ZVAL_ADDR CARG3, val_addr
+			if (RETURN_VALUE_USED(opline)) {
+				|	LOAD_ZVAL_ADDR CARG4, res_addr
+			} else {
+				|	mov CARG4, xzr
+			}
+
+			|	EXT_CALL zend_jit_assign_to_typed_prop, REG0
+
+			if ((opline+1)->op1_type == IS_CONST) {
+				|	// TODO: ???
+				|	// if (Z_TYPE_P(value) == orig_type) {
+				|	// CACHE_PTR_EX(cache_slot + 2, NULL);
+			}
+
+			if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
+			 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+				|	b >8
+			} else {
+				|	b >9
+			}
+			|.code
+		}
+	} else {
+		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, prop_info->offset);
+		if (!ce || ce_is_instanceof || !(ce->ce_flags & ZEND_ACC_IMMUTABLE) || ce->__get || ce->__set) {
+			// Undefined property with magic __get()/__set()
+			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+				|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
+				|	IF_TYPE TMP2w, IS_UNDEF, &exit_addr
+			} else {
+				|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
+				|	IF_TYPE TMP2w, IS_UNDEF, >5
+				needs_slow_path = 1;
+			}
+		}
+		if (ZEND_TYPE_IS_SET(prop_info->type)) {
+			uint32_t info = val_info;
+
+			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
+			|	SET_EX_OPLINE opline, REG0
+			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
+				|	LOAD_ADDR FCARG2x, prop_info
+			} else {
+				int prop_info_offset =
+					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
+
+				|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
+				|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
+				|	SAFE_MEM_ACC_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
+			}
+			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
+			|	LOAD_ZVAL_ADDR CARG3, val_addr
+			if (RETURN_VALUE_USED(opline)) {
+				|	LOAD_ZVAL_ADDR CARG4, res_addr
+			} else {
+				|	mov CARG4, xzr
+			}
+
+			|	EXT_CALL zend_jit_assign_to_typed_prop, REG0
+
+			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+				info |= MAY_BE_RC1|MAY_BE_RCN;
+			}
+
+			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		}
+	}
+
+	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
+		// value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES());
+		if (opline->result_type == IS_UNUSED) {
+			if (!zend_jit_assign_to_variable_call(Dst, opline, prop_addr, prop_addr, -1, -1, (opline+1)->op1_type, val_addr, val_info, res_addr, 0)) {
+				return 0;
+			}
+		} else {
+			if (!zend_jit_assign_to_variable(Dst, opline, prop_addr, prop_addr, -1, -1, (opline+1)->op1_type, val_addr, val_info, res_addr, 0)) {
+				return 0;
+			}
+		}
+	}
+
+	if (needs_slow_path) {
+		|.cold_code
+		|5:
+		|	SET_EX_OPLINE opline, REG0
+		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
+		|	LOAD_ADDR FCARG2x, name
+
+		|	LOAD_ZVAL_ADDR CARG3, val_addr
+		|	ldr CARG4, EX->run_time_cache
+		|	ADD_SUB_64_WITH_CONST_32 add, CARG4, CARG4, opline->extended_value, TMP1
+		if (RETURN_VALUE_USED(opline)) {
+			|	LOAD_ZVAL_ADDR CARG5, res_addr
+		} else {
+			|	mov CARG5, xzr
+		}
+
+		|	EXT_CALL zend_jit_assign_obj_helper, REG0
+
+		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+			val_info |= MAY_BE_RC1|MAY_BE_RCN;
+		}
+
+		|8:
+		|	// FREE_OP_DATA();
+		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+		|	b >9
+		|.code
+	}
+
+	|9:
+	if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) {
+		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
+	}
+
+	if (may_throw) {
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_free(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, int may_throw)
+{
+	zend_jit_addr op1_addr = OP1_ADDR();
+
+	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
+		if (may_throw) {
+			|	SET_EX_OPLINE opline, REG0
+		}
+		if (opline->opcode == ZEND_FE_FREE && (op1_info & (MAY_BE_OBJECT|MAY_BE_REF))) {
+			if (op1_info & MAY_BE_ARRAY) {
+				|	IF_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
+			}
+			|	SAFE_MEM_ACC_WITH_UOFFSET_32 ldr, FCARG1w, FP, (opline->op1.var + offsetof(zval, u2.fe_iter_idx)), TMP1
+			|	mvn TMP1w, wzr // TODO: DynAsm fails loading #-1
+			|	cmp FCARG1w, TMP1w
+			|	beq >7
+			|	EXT_CALL zend_hash_iterator_del, REG0
+			|7:
+		}
+		|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline, ZREG_TMP1, ZREG_TMP2
+		if (may_throw) {
+			if (!zend_jit_check_exception(Dst)) {
+				return 0;
+			}
+		}
+	}
+	return 1;
+}
+
+static int zend_jit_echo(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
+{
+	if (opline->op1_type == IS_CONST) {
+		zval *zv;
+		size_t len;
+
+		zv = RT_CONSTANT(opline, opline->op1);
+		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
+		len = Z_STRLEN_P(zv);
+
+		if (len > 0) {
+			const char *str = Z_STRVAL_P(zv);
+
+			|	SET_EX_OPLINE opline, REG0
+			|	LOAD_ADDR CARG1, str
+			|	LOAD_64BIT_VAL CARG2, len
+			|	EXT_CALL zend_write, REG0
+			if (!zend_jit_check_exception(Dst)) {
+				return 0;
+			}
+		}
+	} else {
+		zend_jit_addr op1_addr = OP1_ADDR();
+
+		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
+
+		|	SET_EX_OPLINE opline, REG0
+		|	GET_ZVAL_PTR REG0, op1_addr, TMP1
+		|	add CARG1, REG0, #offsetof(zend_string, val)
+		|	ldr CARG2, [REG0, #offsetof(zend_string, len)]
+		|	EXT_CALL zend_write, REG0
+		if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+			|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline, ZREG_TMP1, ZREG_TMP2
+		}
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int zend_jit_strlen(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr)
+{
+	zend_jit_addr res_addr = RES_ADDR();
+
+	if (opline->op1_type == IS_CONST) {
+		zval *zv;
+		size_t len;
+
+		zv = RT_CONSTANT(opline, opline->op1);
+		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
+		len = Z_STRLEN_P(zv);
+
+		|	SET_ZVAL_LVAL res_addr, len, TMP1, TMP2
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+	} else {
+		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
+
+		|	GET_ZVAL_PTR REG0, op1_addr, TMP1
+		|	ldr REG0, [REG0, #offsetof(zend_string, len)]
+		|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	}
+	return 1;
+}
+
+static int zend_jit_count(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, int may_throw)
+{
+	zend_jit_addr res_addr = RES_ADDR();
+
+	if (opline->op1_type == IS_CONST) {
+		zval *zv;
+		zend_long count;
+
+		zv = RT_CONSTANT(opline, opline->op1);
+		ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY);
+		count = zend_hash_num_elements(Z_ARRVAL_P(zv));
+
+		|	SET_ZVAL_LVAL res_addr, count, TMP1, TMP2
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+	} else {
+		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY);
+		// Note: See the implementation of ZEND_COUNT in Zend/zend_vm_def.h - arrays do not contain IS_UNDEF starting in php 8.1+.
+
+		|	GET_ZVAL_PTR REG0, op1_addr, TMP1
+		// Sign-extend the 32-bit value to a potentially 64-bit zend_long
+		|	ldr REG0w, [REG0, #offsetof(HashTable, nNumOfElements)]
+		|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
+		|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
+	}
+
+	if (may_throw) {
+		return zend_jit_check_exception(Dst);
+	}
+	return 1;
+}
+
+static int zend_jit_load_this(dasm_State **Dst, uint32_t var)
+{
+	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
+
+	|	ldr FCARG1x, EX->This.value.ptr
+	|	SET_ZVAL_PTR var_addr, FCARG1x, TMP1
+	|	SET_ZVAL_TYPE_INFO var_addr, IS_OBJECT_EX, TMP1w, TMP2
+	|	GC_ADDREF FCARG1x, TMP1w
+	return 1;
+}
+
+static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool check_only)
+{
+	if (!op_array->scope || (op_array->fn_flags & ZEND_ACC_STATIC)) {
+		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+			if (!JIT_G(current_frame) ||
+			    !TRACE_FRAME_IS_THIS_CHECKED(JIT_G(current_frame))) {
+
+				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
+				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+				if (!exit_addr) {
+					return 0;
+				}
+
+				|	ldrb TMP1w, EX->This.u1.v.type
+				|	cmp TMP1w, #IS_OBJECT
+				|	bne &exit_addr
+
+				if (JIT_G(current_frame)) {
+					TRACE_FRAME_SET_THIS_CHECKED(JIT_G(current_frame));
+				}
+			}
+		} else {
+
+			|	ldrb TMP1w, EX->This.u1.v.type
+			|	cmp TMP1w, #IS_OBJECT
+			|	bne >1
+			|.cold_code
+			|1:
+			|	SET_EX_OPLINE opline, REG0
+			|	b ->invalid_this
+			|.code
+		}
+	}
+
+	if (!check_only) {
+		if (!zend_jit_load_this(Dst, opline->result.var)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int zend_jit_hash_jmp(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, HashTable *jumptable, int default_b, const void *default_label, const zend_op *next_opline, zend_jit_trace_info *trace_info)
+{
+	uint32_t count;
+	Bucket *p;
+	const zend_op *target;
+	int b;
+	int32_t exit_point;
+	const void *exit_addr;
+
+	if (default_label) {
+		|	cbz REG0, &default_label
+	} else if (next_opline) {
+		|	cbz REG0, >3
+	} else {
+		|	cbz REG0, =>default_b
+	}
+	|	LOAD_ADDR FCARG1x, jumptable
+	|	ldr TMP1, [FCARG1x, #offsetof(HashTable, arData)]
+	|	sub REG0, REG0, TMP1
+	|	mov FCARG1x, #(sizeof(Bucket) / sizeof(void*))
+	|	sdiv REG0, REG0, FCARG1x
+	|	adr FCARG1x, >4
+	|	ldr TMP1, [FCARG1x, REG0]
+	|	br TMP1
+
+	|.jmp_table
+	|.align 8
+	|4:
+	if (trace_info) {
+		trace_info->jmp_table_size += zend_hash_num_elements(jumptable);
+	}
+
+	count = jumptable->nNumUsed;
+	p = jumptable->arData;
+	do {
+		if (Z_TYPE(p->val) == IS_UNDEF) {
+			if (default_label) {
+				|	.addr &default_label
+			} else if (next_opline) {
+				|	.addr >3
+			} else {
+				|	.addr =>default_b
+			}
+		} else {
+			target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val));
+			if (!next_opline) {
+				b = ssa->cfg.map[target - op_array->opcodes];
+				|	.addr =>b
+			} else if (next_opline == target) {
+				|	.addr >3
+			} else {
+				exit_point = zend_jit_trace_get_exit_point(target, 0);
+				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+				|	.addr &exit_addr
+			}
+		}
+		p++;
+		count--;
+	} while (count);
+	|.code
+
+	return 1;
+}
+
+static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info)
+{
+	HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
+	const zend_op *next_opline = NULL;
+
+	if (trace) {
+		ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
+		ZEND_ASSERT(trace->opline != NULL);
+		next_opline = trace->opline;
+	}
+
+	if (opline->op1_type == IS_CONST) {
+		zval *zv = RT_CONSTANT(opline, opline->op1);
+		zval *jump_zv = NULL;
+		int b;
+
+		if (opline->opcode == ZEND_SWITCH_LONG) {
+			if (Z_TYPE_P(zv) == IS_LONG) {
+				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
+			}
+		} else if (opline->opcode == ZEND_SWITCH_STRING) {
+			if (Z_TYPE_P(zv) == IS_STRING) {
+				jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(zv), 1);
+			}
+		} else if (opline->opcode == ZEND_MATCH) {
+			if (Z_TYPE_P(zv) == IS_LONG) {
+				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
+			} else if (Z_TYPE_P(zv) == IS_STRING) {
+				jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(zv), 1);
+			}
+		} else {
+			ZEND_UNREACHABLE();
+		}
+		if (next_opline) {
+			const zend_op *target;
+
+			if (jump_zv != NULL) {
+				target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
+			} else {
+				target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
+			}
+			ZEND_ASSERT(target == next_opline);
+		} else {
+			if (jump_zv != NULL) {
+				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes];
+			} else {
+				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
+			}
+			|	b =>b
+		}
+	} else {
+		zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
+		uint32_t op1_info = OP1_INFO();
+		zend_jit_addr op1_addr = OP1_ADDR();
+		const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
+		const zend_op *target;
+		int default_b = next_opline ? -1 : ssa->cfg.map[default_opline - op_array->opcodes];
+		int b;
+		int32_t exit_point;
+		const void *fallback_label = NULL;
+		const void *default_label = NULL;
+		const void *exit_addr;
+
+		if (next_opline) {
+			if (next_opline != opline + 1) {
+				exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
+				fallback_label = zend_jit_trace_get_exit_addr(exit_point);
+			}
+			if (next_opline != default_opline) {
+				exit_point = zend_jit_trace_get_exit_point(default_opline, 0);
+				default_label = zend_jit_trace_get_exit_addr(exit_point);
+			}
+		}
+
+		if (opline->opcode == ZEND_SWITCH_LONG) {
+			if (op1_info & MAY_BE_LONG) {
+				if (op1_info & MAY_BE_REF) {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >1, ZREG_TMP1
+					|	GET_ZVAL_LVAL ZREG_FCARG2x, op1_addr, TMP1
+					|.cold_code
+					|1:
+					|	// ZVAL_DEREF(op)
+					if (fallback_label) {
+						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label, ZREG_TMP1
+					} else {
+						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3, ZREG_TMP1
+					}
+					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
+					if (fallback_label) {
+						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
+						|	IF_NOT_Z_TYPE TMP1, IS_LONG, &fallback_label, TMP2w
+					} else {
+						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
+						|	IF_NOT_Z_TYPE TMP1, IS_LONG, >3, TMP2w
+					}
+					|	ldr FCARG2x, [FCARG2x, #offsetof(zend_reference, val.value.lval)]
+					|	b >2
+					|.code
+					|2:
+				} else {
+					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
+						if (fallback_label) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &fallback_label, ZREG_TMP1
+						} else {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1
+						}
+					}
+					|	GET_ZVAL_LVAL ZREG_FCARG2x, op1_addr, TMP1
+				}
+				if (HT_IS_PACKED(jumptable)) {
+					uint32_t count = jumptable->nNumUsed;
+					Bucket *p = jumptable->arData;
+
+					|	CMP_64_WITH_CONST_32 FCARG2x, jumptable->nNumUsed, TMP1
+					if (default_label) {
+						|	bhs &default_label
+					} else if (next_opline) {
+						|	bhs >3
+					} else {
+						|	bhs =>default_b
+					}
+					|	adr REG0, >4
+					|	ldr TMP1, [REG0, FCARG2x, lsl #3]
+					|	br TMP1
+
+					|.jmp_table
+					|.align 8
+					|4:
+					if (trace_info) {
+						trace_info->jmp_table_size += count;
+					}
+					p = jumptable->arData;
+					do {
+						if (Z_TYPE(p->val) == IS_UNDEF) {
+							if (default_label) {
+								|	.addr &default_label
+							} else if (next_opline) {
+								|	.addr >3
+							} else {
+								|	.addr =>default_b
+							}
+						} else {
+							target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val));
+							if (!next_opline) {
+								b = ssa->cfg.map[target - op_array->opcodes];
+								|	.addr =>b
+							} else if (next_opline == target) {
+								|	.addr >3
+							} else {
+								exit_point = zend_jit_trace_get_exit_point(target, 0);
+								exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+								|	.addr &exit_addr
+							}
+						}
+						p++;
+						count--;
+					} while (count);
+					|.code
+					|3:
+				} else {
+					|	LOAD_ADDR FCARG1x, jumptable
+					|	EXT_CALL zend_hash_index_find, REG0
+					|	mov REG0, RETVALx
+					if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
+						return 0;
+					}
+					|3:
+				}
+			}
+		} else if (opline->opcode == ZEND_SWITCH_STRING) {
+			if (op1_info & MAY_BE_STRING) {
+				if (op1_info & MAY_BE_REF) {
+					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >1, ZREG_TMP1
+					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
+					|.cold_code
+					|1:
+					|	// ZVAL_DEREF(op)
+					if (fallback_label) {
+						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label, ZREG_TMP1
+					} else {
+						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3, ZREG_TMP1
+					}
+					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
+					if (fallback_label) {
+						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
+						|	IF_NOT_Z_TYPE TMP1, IS_STRING, &fallback_label, TMP2w
+					} else {
+						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
+						|	IF_NOT_Z_TYPE TMP1, IS_STRING, >3, TMP2w
+					}
+					|	ldr FCARG2x, [FCARG2x, #offsetof(zend_reference, val.value.ptr)]
+					|	b >2
+					|.code
+					|2:
+				} else {
+					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_STRING)) {
+						if (fallback_label) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &fallback_label, ZREG_TMP1
+						} else {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3, ZREG_TMP1
+						}
+					}
+					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
+				}
+				|	LOAD_ADDR FCARG1x, jumptable
+				|	EXT_CALL zend_hash_find, REG0
+				|	mov REG0, RETVALx
+				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
+					return 0;
+				}
+				|3:
+			}
+		} else if (opline->opcode == ZEND_MATCH) {
+			if (op1_info & (MAY_BE_LONG|MAY_BE_STRING)) {
+				if (op1_info & MAY_BE_REF) {
+					|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
+					|	ZVAL_DEREF FCARG2x, op1_info, TMP1w
+					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2x, 0);
+				}
+				|	LOAD_ADDR FCARG1x, jumptable
+				if (op1_info & MAY_BE_LONG) {
+					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
+						if (op1_info & MAY_BE_STRING) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >5, ZREG_TMP1
+						} else if (op1_info & MAY_BE_UNDEF) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
+						} else if (default_label) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &default_label, ZREG_TMP1
+						} else if (next_opline) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1
+						} else {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, =>default_b, ZREG_TMP1
+						}
+					}
+					|	GET_ZVAL_LVAL ZREG_FCARG2x, op1_addr, TMP1
+					|	EXT_CALL zend_hash_index_find, REG0
+					|	mov REG0, RETVALx
+					if (op1_info & MAY_BE_STRING) {
+						|	b >2
+					}
+				}
+				if (op1_info & MAY_BE_STRING) {
+					|5:
+					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_STRING))) {
+						if (op1_info & MAY_BE_UNDEF) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1
+						} else if (default_label) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &default_label, ZREG_TMP1
+						} else if (next_opline) {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3, ZREG_TMP1
+						} else {
+							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, =>default_b, ZREG_TMP1
+						}
+					}
+					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
+					|	EXT_CALL zend_hash_find, REG0
+					|	mov REG0, RETVALx
+				}
+				|2:
+				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
+					return 0;
+				}
+			}
+			if (op1_info & MAY_BE_UNDEF) {
+				|6:
+				if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_STRING))) {
+					if (default_label) {
+						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, &default_label, ZREG_TMP1
+					} else if (next_opline) {
+						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >3, ZREG_TMP1
+					} else {
+						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, =>default_b, ZREG_TMP1
+					}
+				}
+				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
+				|	SET_EX_OPLINE opline, REG0
+				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
+				|	EXT_CALL zend_jit_undefined_op_helper, REG0
+				if (!zend_jit_check_exception_undef_result(Dst, opline)) {
+					return 0;
+				}
+			}
+			if (default_label) {
+				|	b &default_label
+			} else if (next_opline) {
+				|	b >3
+			} else {
+				|	b =>default_b
+			}
+			|3:
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	}
+	return 1;
+}
+
+static bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info)
+{
+	zend_arg_info *arg_info = &op_array->arg_info[-1];
+	ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
+	zend_jit_addr op1_addr = OP1_ADDR();
+	bool needs_slow_check = 1;
+	bool slow_check_in_cold = 1;
+	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
+
+	if (type_mask == 0) {
+		slow_check_in_cold = 0;
+	} else {
+		if (((op1_info & MAY_BE_ANY) & type_mask) == 0) {
+			slow_check_in_cold = 0;
+		} else if (((op1_info & MAY_BE_ANY) | type_mask) == type_mask) {
+			needs_slow_check = 0;
+		} else if (is_power_of_two(type_mask)) {
+			uint32_t type_code = concrete_type(type_mask);
+			|	IF_NOT_ZVAL_TYPE op1_addr, type_code, >7, ZREG_TMP1
+		} else {
+			|	mov REG2w, #1
+			|	GET_ZVAL_TYPE REG1w, op1_addr, TMP1
+			|	lsl REG2w, REG2w, REG1w
+			|	TST_32_WITH_CONST REG2w, type_mask, TMP1w
+			|	beq >7
+		}
+	}
+	if (needs_slow_check) {
+		if (slow_check_in_cold) {
+			|.cold_code
+			|7:
+		}
+		|	SET_EX_OPLINE opline, REG1
+		if (op1_info & MAY_BE_UNDEF) {
+			|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >8, ZREG_TMP1
+			|	LOAD_32BIT_VAL FCARG1x, opline->op1.var
+			|	EXT_CALL zend_jit_undefined_op_helper, REG0
+			|	LOAD_ADDR_ZTS REG0, executor_globals, uninitialized_zval
+		}
+		|8:
+		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+		|	ldr FCARG2x, EX->func
+		|	LOAD_ADDR CARG3, (ptrdiff_t)arg_info
+		|	ldr REG0, EX->run_time_cache
+		|	ADD_SUB_64_WITH_CONST_32 add, CARG4, REG0, opline->op2.num, TMP1
+		|	EXT_CALL zend_jit_verify_return_slow, REG0
+		if (!zend_jit_check_exception(Dst)) {
+			return 0;
+		}
+		if (slow_check_in_cold) {
+			|	b >9
+			|.code
+		}
+	}
+	|9:
+	return 1;
+}
+
+static int zend_jit_isset_isempty_cv(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr,  zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+	// TODO: support for empty() ???
+	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
+
+	if (op1_info & MAY_BE_REF) {
+		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1x || Z_OFFSET(op1_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
+			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+		}
+		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
+		|1:
+	}
+
+	if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) {
+		if (exit_addr) {
+			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPZ);
+		} else if (smart_branch_opcode) {
+			if (smart_branch_opcode == ZEND_JMPNZ) {
+				|	b =>target_label
+			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+				|	b =>target_label2
+			}
+		} else {
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
+		}
+	} else if (!(op1_info & (MAY_BE_ANY - MAY_BE_NULL))) {
+		if (exit_addr) {
+			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPNZ);
+		} else if (smart_branch_opcode) {
+			if (smart_branch_opcode != ZEND_JMPNZ) {
+				|	b =>target_label
+			}
+		} else {
+			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
+		}
+	} else {
+		ZEND_ASSERT(Z_MODE(op1_addr) == IS_MEM_ZVAL);
+		|	SAFE_MEM_ACC_WITH_UOFFSET_BYTE ldrb, TMP1w, Rx(Z_REG(op1_addr)), (Z_OFFSET(op1_addr)+offsetof(zval, u1.v.type)), TMP1
+		|	cmp TMP1w, #IS_NULL
+		if (exit_addr) {
+			if (smart_branch_opcode == ZEND_JMPNZ) {
+				|	bgt &exit_addr
+			} else {
+				|	ble &exit_addr
+			}
+		} else if (smart_branch_opcode) {
+			if (smart_branch_opcode == ZEND_JMPZ) {
+				|	ble =>target_label
+			} else if (smart_branch_opcode == ZEND_JMPNZ) {
+				|	bgt =>target_label
+			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+				|	ble =>target_label
+				|	b =>target_label2
+			} else {
+				ZEND_UNREACHABLE();
+			}
+		} else {
+			|	cset REG0w, gt
+			|	add REG0w, REG0w, #IS_FALSE
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_fe_reset(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
+{
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+	if (opline->op1_type == IS_CONST) {
+		zval *zv = RT_CONSTANT(opline, opline->op1);
+
+		|	ZVAL_COPY_CONST res_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
+		if (Z_REFCOUNTED_P(zv)) {
+			|	ADDREF_CONST zv, REG0, TMP1
+		}
+	} else {
+		zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
+
+		|	// ZVAL_COPY(res, value);
+		|	ZVAL_COPY_VALUE res_addr, -1, op1_addr, op1_info, ZREG_REG0, ZREG_FCARG1x, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		if (opline->op1_type == IS_CV) {
+			|	TRY_ADDREF op1_info, REG0w, FCARG1x, TMP1w
+		}
+	}
+	|	// Z_FE_POS_P(res) = 0;
+	|	SAFE_MEM_ACC_WITH_UOFFSET_32 str, wzr, FP, (opline->result.var + offsetof(zval, u2.fe_pos)), TMP1
+
+	return 1;
+}
+
+static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint32_t op2_info, unsigned int target_label, zend_uchar exit_opcode, const void *exit_addr)
+{
+	zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
+
+	|	// array = EX_VAR(opline->op1.var);
+	|	// fe_ht = Z_ARRVAL_P(array);
+	|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
+	|	// pos = Z_FE_POS_P(array);
+	|	SAFE_MEM_ACC_WITH_UOFFSET_32 ldr, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1
+	|	// p = fe_ht->arData + pos;
+	||	ZEND_ASSERT(sizeof(Bucket) == 32);
+	|	mov FCARG2w, REG0w
+	|	ldr TMP1, [FCARG1x, #offsetof(zend_array, arData)]
+	|	add FCARG2x, TMP1, FCARG2x, lsl #5
+	|1:
+	|	// if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
+	|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, nNumUsed)]
+	|	cmp TMP1w, REG0w
+	|	// ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+	|   // ZEND_VM_CONTINUE();
+	if (exit_addr) {
+		if (exit_opcode == ZEND_JMP) {
+			|	bls &exit_addr
+		} else {
+			|	bls >3
+		}
+	} else {
+		|	bls =>target_label
+	}
+	|	// pos++;
+	|	add REG0w, REG0w, #1
+	|	// value_type = Z_TYPE_INFO_P(value);
+	|	// if (EXPECTED(value_type != IS_UNDEF)) {
+	if (!exit_addr || exit_opcode == ZEND_JMP) {
+		|	IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, >3, TMP1w
+	} else {
+		|	IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, &exit_addr, TMP1w
+	}
+	|	// p++;
+	|	add FCARG2x, FCARG2x, #sizeof(Bucket)
+	|	b <1
+	|3:
+
+	if (!exit_addr || exit_opcode == ZEND_JMP) {
+		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2x, 0);
+		zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
+		uint32_t val_info;
+
+		|	// Z_FE_POS_P(array) = pos + 1;
+		|	SAFE_MEM_ACC_WITH_UOFFSET_32 str, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1
+
+		if (RETURN_VALUE_USED(opline)) {
+			zend_jit_addr res_addr = RES_ADDR();
+
+			if ((op1_info & MAY_BE_ARRAY_KEY_LONG)
+			 && (op1_info & MAY_BE_ARRAY_KEY_STRING)) {
+				|	// if (!p->key) {
+				|	ldr REG0, [FCARG2x, #offsetof(Bucket, key)]
+				|	cbz REG0, >2
+			}
+			if (op1_info & MAY_BE_ARRAY_KEY_STRING) {
+				|	// ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key);
+				|	ldr REG0, [FCARG2x, #offsetof(Bucket, key)]
+				|	SET_ZVAL_PTR res_addr, REG0, TMP1
+				|	ldr TMP1w, [REG0, #offsetof(zend_refcounted, gc.u.type_info)]
+				|	TST_32_WITH_CONST TMP1w, IS_STR_INTERNED, TMP2w
+				|	beq >1
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING, TMP1w, TMP2
+				|	b >3
+				|1:
+				|	GC_ADDREF REG0, TMP1w
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX, TMP1w, TMP2
+
+				if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
+				    |	b >3
+					|2:
+				}
+			}
+			if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
+				|	// ZVAL_LONG(EX_VAR(opline->result.var), p->h);
+				|	ldr REG0, [FCARG2x, #offsetof(Bucket, h)]
+				|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
+				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
+			}
+			|3:
+		}
+
+		val_info = ((op1_info & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+		if (val_info & MAY_BE_ARRAY) {
+			val_info |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+		}
+		if (op1_info & MAY_BE_ARRAY_OF_REF) {
+			val_info |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY |
+				MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+		} else if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+			val_info |= MAY_BE_RC1 | MAY_BE_RCN;
+		}
+
+		if (opline->op2_type == IS_CV) {
+			|	// zend_assign_to_variable(variable_ptr, value, IS_CV, EX_USES_STRICT_TYPES());
+			if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, op2_info, -1, IS_CV, val_addr, val_info, 0, 1)) {
+				return 0;
+			}
+		} else {
+			|	// ZVAL_COPY(res, value);
+			|	ZVAL_COPY_VALUE var_addr, -1, val_addr, val_info, ZREG_REG0, ZREG_FCARG1x, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+			|	TRY_ADDREF val_info, REG0w, FCARG1x, TMP1w
+		}
+	}
+
+	return 1;
+}
+
+static int zend_jit_fetch_constant(dasm_State          **Dst,
+                                   const zend_op        *opline,
+                                   const zend_op_array  *op_array,
+                                   zend_ssa             *ssa,
+                                   const zend_ssa_op    *ssa_op)
+{
+	zval *zv = RT_CONSTANT(opline, opline->op2) + 1;
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+	zend_jit_addr const_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
+	uint32_t res_info = RES_INFO();
+
+	|	// c = CACHED_PTR(opline->extended_value);
+	|	ldr FCARG1x, EX->run_time_cache
+	|	SAFE_MEM_ACC_WITH_UOFFSET ldr, REG0, FCARG1x, opline->extended_value, TMP1
+	|	// if (c != NULL)
+	|	cbz REG0, >9
+	|	// if (!IS_SPECIAL_CACHE_VAL(c))
+	||	ZEND_ASSERT(CACHE_SPECIAL == 1);
+	|	TST_64_WITH_ONE REG0
+	|	bne >9
+	|8:
+
+	if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) {
+		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
+		uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+		int32_t exit_point;
+		const void *exit_addr = NULL;
+
+		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
+		SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
+		exit_point = zend_jit_trace_get_exit_point(opline+1, 0);
+		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+		if (!exit_addr) {
+			return 0;
+		}
+		res_info &= ~MAY_BE_GUARD;
+		ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
+
+		uint32_t type = concrete_type(res_info);
+
+		if (type < IS_STRING) {
+			|	IF_NOT_ZVAL_TYPE const_addr, type, &exit_addr, ZREG_TMP1
+		} else {
+			|	GET_ZVAL_TYPE_INFO REG2w, const_addr, TMP1
+			|	IF_NOT_TYPE REG2w, type, &exit_addr
+		}
+		|	ZVAL_COPY_VALUE_V res_addr, -1, const_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0
+		if (type < IS_STRING) {
+			|	SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2
+		} else {
+			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
+			|	TRY_ADDREF res_info, REG2w, REG1, TMP1w
+		}
+	} else {
+		|	ZVAL_COPY_VALUE res_addr, MAY_BE_ANY, const_addr, MAY_BE_ANY, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
+		|	TRY_ADDREF MAY_BE_ANY, REG0w, REG1, TMP1w
+	}
+
+	|.cold_code
+	|9:
+	|	// SAVE_OPLINE();
+	|	SET_EX_OPLINE opline, REG0
+	|	// zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num OPLINE_CC EXECUTE_DATA_CC);
+	|	LOAD_ADDR FCARG1x, zv
+	|	LOAD_32BIT_VAL FCARG2w, opline->op1.num
+	|	EXT_CALL zend_jit_get_constant, REG0
+	|	mov REG0, RETVALx
+	|	// ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+	|	cbnz REG0, <8
+	|	b ->exception_handler
+	|.code
+	return 1;
+}
+
+static int zend_jit_in_array(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr,  zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+{
+	HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
+	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
+
+	ZEND_ASSERT(opline->op1_type != IS_VAR && opline->op1_type != IS_TMP_VAR);
+	ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_STRING);
+
+	|	// result = zend_hash_find_ex(ht, Z_STR_P(op1), OP1_TYPE == IS_CONST);
+	|	LOAD_ADDR FCARG1x, ht
+	if (opline->op1_type != IS_CONST) {
+		|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
+		|	EXT_CALL zend_hash_find, REG0
+	} else {
+		zend_string *str = Z_STR_P(RT_CONSTANT(opline, opline->op1));
+		|	LOAD_ADDR FCARG2x, str
+		|	EXT_CALL _zend_hash_find_known_hash, REG0
+	}
+	if (exit_addr) {
+		if (smart_branch_opcode == ZEND_JMPZ) {
+			|	cbz RETVALx, &exit_addr
+		} else {
+			|	cbnz RETVALx, &exit_addr
+		}
+	} else if (smart_branch_opcode) {
+		if (smart_branch_opcode == ZEND_JMPZ) {
+			|	cbz RETVALx, =>target_label
+		} else if (smart_branch_opcode == ZEND_JMPNZ) {
+			|	cbnz RETVALx, =>target_label
+		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
+			|	cbz RETVALx, =>target_label
+			|	b =>target_label2
+		} else {
+			ZEND_UNREACHABLE();
+		}
+	} else {
+		|	tst RETVALx, RETVALx
+		|	cset REG0w, ne
+		|	add REG0w, REG0w, #IS_FALSE
+		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
+	}
+
+	return 1;
+}
+
+static bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_addr)
+{
+	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
+	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+	if (!exit_addr) {
+		return 0;
+	}
+	|	IF_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr, ZREG_TMP1
+
+	return 1;
+}
+
+static bool zend_jit_fetch_reference(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr, bool add_ref_guard, bool add_type_guard)
+{
+	zend_jit_addr var_addr = *var_addr_ptr;
+	uint32_t var_info = *var_info_ptr;
+	const void *exit_addr = NULL;
+
+	if (add_ref_guard || add_type_guard) {
+		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
+
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+		if (!exit_addr) {
+			return 0;
+		}
+	}
+
+	if (add_ref_guard) {
+		|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr, ZREG_TMP1
+	}
+	if (opline->opcode == ZEND_INIT_METHOD_CALL && opline->op1_type == IS_VAR) {
+		/* Hack: Convert reference to regular value to simplify JIT code for INIT_METHOD_CALL */
+		if (Z_REG(var_addr) != ZREG_FCARG1x || Z_OFFSET(var_addr) != 0) {
+			|	LOAD_ZVAL_ADDR FCARG1x, var_addr
+		}
+		|	EXT_CALL zend_jit_unref_helper, REG0
+	} else {
+		|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
+		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, offsetof(zend_reference, val));
+		*var_addr_ptr = var_addr;
+	}
+
+	if (var_type != IS_UNKNOWN) {
+		var_type &= ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED);
+	}
+	if (add_type_guard
+	 && var_type != IS_UNKNOWN
+	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
+		|	IF_NOT_ZVAL_TYPE var_addr, var_type, &exit_addr, ZREG_TMP1
+
+		ZEND_ASSERT(var_info & (1 << var_type));
+		if (var_type < IS_STRING) {
+			var_info = (1 << var_type);
+		} else if (var_type != IS_ARRAY) {
+			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
+		} else {
+			var_info = MAY_BE_ARRAY | (var_info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY|MAY_BE_RC1|MAY_BE_RCN));
+		}
+
+		*var_info_ptr = var_info;
+	} else {
+		var_info &= ~MAY_BE_REF;
+		*var_info_ptr = var_info;
+	}
+
+	return 1;
+}
+
+static bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr, bool add_indirect_guard)
+{
+	zend_jit_addr var_addr = *var_addr_ptr;
+	uint32_t var_info = *var_info_ptr;
+	int32_t exit_point;
+	const void *exit_addr;
+
+	if (add_indirect_guard) {
+		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
+		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+		if (!exit_addr) {
+			return 0;
+		}
+		|	IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr, ZREG_TMP1
+		|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
+	} else {
+		/* May be already loaded into FCARG1a or RAX by previus FETCH_OBJ_W/DIM_W */
+		if (opline->op1_type != IS_VAR ||
+				(opline-1)->result_type != IS_VAR  ||
+				(opline-1)->result.var != opline->op1.var ||
+				(opline-1)->op2_type == IS_VAR ||
+				(opline-1)->op2_type == IS_TMP_VAR) {
+			|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
+		} else if ((opline-1)->opcode == ZEND_FETCH_DIM_W || (opline-1)->opcode == ZEND_FETCH_DIM_RW) {
+			|	mov FCARG1x, REG0
+		}
+	}
+	*var_info_ptr &= ~MAY_BE_INDIRECT;
+	var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
+	*var_addr_ptr = var_addr;
+
+	if (var_type != IS_UNKNOWN) {
+		var_type &= ~(IS_TRACE_INDIRECT|IS_TRACE_PACKED);
+	}
+	if (!(var_type & IS_TRACE_REFERENCE)
+	 && var_type != IS_UNKNOWN
+	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
+		exit_point = zend_jit_trace_get_exit_point(opline, 0);
+		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+
+		if (!exit_addr) {
+			return 0;
+		}
+
+		|	IF_NOT_Z_TYPE FCARG1x, var_type, &exit_addr, TMP1w
+
+		//var_info = zend_jit_trace_type_to_info_ex(var_type, var_info);
+		ZEND_ASSERT(var_info & (1 << var_type));
+		if (var_type < IS_STRING) {
+			var_info = (1 << var_type);
+		} else if (var_type != IS_ARRAY) {
+			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
+		} else {
+			var_info = MAY_BE_ARRAY | (var_info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY|MAY_BE_RC1|MAY_BE_RCN));
+		}
+
+		*var_info_ptr = var_info;
+	}
+
+	return 1;
+}
+
+static bool zend_jit_may_reuse_reg(const zend_op *opline, const zend_ssa_op *ssa_op, zend_ssa *ssa, int def_var, int use_var)
+{
+	if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) {
+		return 0;
+	}
+
+	switch (opline->opcode) {
+		case ZEND_QM_ASSIGN:
+		case ZEND_SEND_VAR:
+		case ZEND_ASSIGN:
+		case ZEND_PRE_INC:
+		case ZEND_PRE_DEC:
+		case ZEND_POST_INC:
+		case ZEND_POST_DEC:
+			return 1;
+		case ZEND_ADD:
+		case ZEND_SUB:
+		case ZEND_MUL:
+		case ZEND_BW_OR:
+		case ZEND_BW_AND:
+		case ZEND_BW_XOR:
+			if (def_var == ssa_op->result_def &&
+			    use_var == ssa_op->op1_use) {
+				return 1;
+			}
+			break;
+		default:
+			break;
+	}
+	return 0;
+}
+
+static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, zend_jit_trace_rec *trace)
+{
+	uint32_t op1_info, op2_info;
+
+	switch (opline->opcode) {
+		case ZEND_QM_ASSIGN:
+		case ZEND_SEND_VAR:
+		case ZEND_SEND_VAL:
+		case ZEND_SEND_VAL_EX:
+		case ZEND_IS_SMALLER:
+		case ZEND_IS_SMALLER_OR_EQUAL:
+		case ZEND_IS_EQUAL:
+		case ZEND_IS_NOT_EQUAL:
+		case ZEND_IS_IDENTICAL:
+		case ZEND_IS_NOT_IDENTICAL:
+		case ZEND_CASE:
+		case ZEND_RETURN:
+			return 1;
+		case ZEND_ASSIGN:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			return
+				opline->op1_type == IS_CV &&
+				!(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_RESOURCE|MAY_BE_REF)) &&
+				!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)));
+		case ZEND_ADD:
+		case ZEND_SUB:
+		case ZEND_MUL:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE)));
+		case ZEND_BW_OR:
+		case ZEND_BW_AND:
+		case ZEND_BW_XOR:
+		case ZEND_SL:
+		case ZEND_SR:
+		case ZEND_MOD:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG));
+		case ZEND_PRE_INC:
+		case ZEND_PRE_DEC:
+		case ZEND_POST_INC:
+		case ZEND_POST_DEC:
+			op1_info = OP1_INFO();
+			op2_info = OP1_DEF_INFO();
+			return opline->op1_type == IS_CV
+				&& !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG))
+				&& (op2_info & MAY_BE_LONG);
+		case ZEND_BOOL:
+		case ZEND_BOOL_NOT:
+		case ZEND_JMPZ:
+		case ZEND_JMPNZ:
+		case ZEND_JMPZNZ:
+		case ZEND_JMPZ_EX:
+		case ZEND_JMPNZ_EX:
+			return 1;
+		case ZEND_FETCH_DIM_R:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (trace
+			 && trace->op1_type != IS_UNKNOWN
+			 && (trace->op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)) == IS_ARRAY) {
+				op1_info &= ~((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY);
+			}
+			return ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) &&
+				(!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || !(op1_info & MAY_BE_RC1)) &&
+					(((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) ||
+					 (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) &&
+						 (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & MAY_BE_RC1))));
+	}
+	return 0;
+}
+
+static bool zend_jit_var_supports_reg(zend_ssa *ssa, int var)
+{
+	if (ssa->vars[var].no_val) {
+		/* we don't need the value */
+		return 0;
+	}
+
+	if (!(JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL)) {
+		/* Disable global register allocation,
+		 * register allocation for SSA variables connected through Phi functions
+		 */
+		if (ssa->vars[var].definition_phi) {
+			return 0;
+		}
+		if (ssa->vars[var].phi_use_chain) {
+			zend_ssa_phi *phi = ssa->vars[var].phi_use_chain;
+			do {
+				if (!ssa->vars[phi->ssa_var].no_val) {
+					return 0;
+				}
+				phi = zend_ssa_next_use_phi(ssa, var, phi);
+			} while (phi);
+		}
+	}
+
+	if (((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_DOUBLE) &&
+	    ((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_LONG)) {
+	    /* bad type */
+		return 0;
+	}
+
+	return 1;
+}
+
+static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, int var)
+{
+	if (!zend_jit_var_supports_reg(ssa, var)) {
+		return 0;
+	}
+
+	if (ssa->vars[var].definition >= 0) {
+		uint32_t def = ssa->vars[var].definition;
+		if (!zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + def, ssa->ops + def, NULL)) {
+			return 0;
+		}
+	}
+
+	if (ssa->vars[var].use_chain >= 0) {
+		int use = ssa->vars[var].use_chain;
+
+		do {
+			if (!zend_ssa_is_no_val_use(op_array->opcodes + use, ssa->ops + use, var) &&
+			    !zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + use, ssa->ops + use, NULL)) {
+				return 0;
+			}
+			use = zend_ssa_next_use(ssa->ops, var, use);
+		} while (use >= 0);
+	}
+
+	return 1;
+}
+
+static bool zend_needs_extra_reg_for_const(const zend_op *opline, zend_uchar op_type, znode_op op)
+{
+||	if (op_type == IS_CONST) {
+||		zval *zv = RT_CONSTANT(opline, op);
+||		if (Z_TYPE_P(zv) == IS_DOUBLE && Z_DVAL_P(zv) != 0) {
+||			return 1;
+||		} else if (Z_TYPE_P(zv) == IS_LONG) {
+||			return 1;
+||		}
+||	}
+	return 0;
+}
+
+static zend_regset zend_jit_get_def_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, bool last_use)
+{
+	uint32_t op1_info, op2_info;
+
+	switch (opline->opcode) {
+		case ZEND_FETCH_DIM_R:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (((opline->op1_type & (IS_TMP_VAR|IS_VAR)) &&
+			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) ||
+			    ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) &&
+			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)))) {
+				return ZEND_REGSET(ZREG_FCARG1x);
+			}
+			break;
+		default:
+			break;
+	}
+
+	return ZEND_REGSET_EMPTY;
+}
+
+static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, bool last_use)
+{
+	uint32_t op1_info, op2_info, res_info;
+	zend_regset regset = ZEND_REGSET_SCRATCH;
+
+	switch (opline->opcode) {
+		case ZEND_NOP:
+		case ZEND_OP_DATA:
+		case ZEND_JMP:
+		case ZEND_RETURN:
+			regset = ZEND_REGSET_EMPTY;
+			break;
+		case ZEND_QM_ASSIGN:
+			if (ssa_op->op1_def == current_var ||
+			    ssa_op->result_def == current_var) {
+				regset = ZEND_REGSET_EMPTY;
+				break;
+			}
+			/* break missing intentionally */
+		case ZEND_SEND_VAL:
+		case ZEND_SEND_VAL_EX:
+			if (ssa_op->op1_use == current_var) {
+				regset = ZEND_REGSET(ZREG_REG0);
+				break;
+			}
+			op1_info = OP1_INFO();
+			if (!(op1_info & MAY_BE_UNDEF)) {
+				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
+					regset = ZEND_REGSET(ZREG_FPR0);
+				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
+					regset = ZEND_REGSET(ZREG_REG0);
+				} else {
+					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2));
+				}
+			}
+			break;
+		case ZEND_SEND_VAR:
+			if (ssa_op->op1_use == current_var ||
+			    ssa_op->op1_def == current_var) {
+				regset = ZEND_REGSET_EMPTY;
+				break;
+			}
+			op1_info = OP1_INFO();
+			if (!(op1_info & MAY_BE_UNDEF)) {
+				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
+					regset = ZEND_REGSET(ZREG_FPR0);
+				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
+				} else {
+					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2));
+					if (op1_info & MAY_BE_REF) {
+						ZEND_REGSET_INCL(regset, ZREG_REG1);
+					}
+				}
+			}
+			break;
+		case ZEND_ASSIGN:
+			if (ssa_op->op2_use == current_var ||
+			    ssa_op->op2_def == current_var ||
+			    ssa_op->op1_def == current_var ||
+			    ssa_op->result_def == current_var) {
+				regset = ZEND_REGSET_EMPTY;
+				break;
+			}
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (opline->op1_type == IS_CV
+			 && !(op2_info & MAY_BE_UNDEF)
+			 && !(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+				if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
+					regset = ZEND_REGSET(ZREG_FPR0);
+				} else if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
+					regset = ZEND_REGSET(ZREG_REG0);
+				} else {
+					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2));
+				}
+			}
+			break;
+		case ZEND_PRE_INC:
+		case ZEND_PRE_DEC:
+		case ZEND_POST_INC:
+		case ZEND_POST_DEC:
+			if (ssa_op->op1_use == current_var ||
+			    ssa_op->op1_def == current_var ||
+			    ssa_op->result_def == current_var) {
+				regset = ZEND_REGSET_EMPTY;
+				break;
+			}
+			op1_info = OP1_INFO();
+			if (opline->op1_type == IS_CV
+			 && (op1_info & MAY_BE_LONG)
+			 && !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
+				regset = ZEND_REGSET_EMPTY;
+				if (op1_info & MAY_BE_DOUBLE) {
+					regset = ZEND_REGSET(ZREG_FPR0);
+				}
+			}
+			break;
+		case ZEND_ADD:
+		case ZEND_SUB:
+		case ZEND_MUL:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
+			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
+
+				regset = ZEND_REGSET_EMPTY;
+				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
+					if (ssa_op->result_def != current_var &&
+					    (ssa_op->op1_use != current_var || !last_use)) {
+						ZEND_REGSET_INCL(regset, ZREG_REG0);
+					}
+					res_info = OP1_INFO();
+					if (res_info & MAY_BE_DOUBLE) {
+						ZEND_REGSET_INCL(regset, ZREG_FPR0);
+						ZEND_REGSET_INCL(regset, ZREG_FPR1);
+					}
+				}
+				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
+					if (ssa_op->result_def != current_var) {
+						ZEND_REGSET_INCL(regset, ZREG_FPR0);
+					}
+				}
+				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
+					if (zend_is_commutative(opline->opcode)) {
+						if (ssa_op->result_def != current_var) {
+							ZEND_REGSET_INCL(regset, ZREG_FPR0);
+						}
+					} else {
+						ZEND_REGSET_INCL(regset, ZREG_FPR0);
+						if (ssa_op->result_def != current_var &&
+						    (ssa_op->op1_use != current_var || !last_use)) {
+							ZEND_REGSET_INCL(regset, ZREG_FPR1);
+						}
+					}
+				}
+				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
+					if (ssa_op->result_def != current_var &&
+					    (ssa_op->op1_use != current_var || !last_use) &&
+					    (!zend_is_commutative(opline->opcode) || ssa_op->op2_use != current_var || !last_use)) {
+						ZEND_REGSET_INCL(regset, ZREG_FPR0);
+					}
+				}
+				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
+				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
+					if (!ZEND_REGSET_IN(regset, ZREG_REG0)) {
+						ZEND_REGSET_INCL(regset, ZREG_REG0);
+					} else {
+						ZEND_REGSET_INCL(regset, ZREG_REG1);
+					}
+				}
+			}
+			break;
+		case ZEND_BW_OR:
+		case ZEND_BW_AND:
+		case ZEND_BW_XOR:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
+			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
+				regset = ZEND_REGSET_EMPTY;
+				if (ssa_op->result_def != current_var &&
+				    (ssa_op->op1_use != current_var || !last_use)) {
+					ZEND_REGSET_INCL(regset, ZREG_REG0);
+				}
+				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
+				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
+					if (!ZEND_REGSET_IN(regset, ZREG_REG0)) {
+						ZEND_REGSET_INCL(regset, ZREG_REG0);
+					} else {
+						ZEND_REGSET_INCL(regset, ZREG_REG1);
+					}
+				}
+			}
+			break;
+		case ZEND_SL:
+		case ZEND_SR:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
+			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
+				regset = ZEND_REGSET_EMPTY;
+				if (ssa_op->result_def != current_var &&
+				    (ssa_op->op1_use != current_var || !last_use)) {
+					ZEND_REGSET_INCL(regset, ZREG_REG0);
+				}
+				if (opline->op2_type != IS_CONST && ssa_op->op2_use != current_var) {
+					ZEND_REGSET_INCL(regset, ZREG_REG1);
+				}
+			}
+			break;
+		case ZEND_MOD:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
+			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
+				regset = ZEND_REGSET_EMPTY;
+				if (opline->op2_type == IS_CONST &&
+				    Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG &&
+				    zend_long_is_power_of_two(Z_LVAL_P(RT_CONSTANT(opline, opline->op2))) &&
+				    OP1_HAS_RANGE() &&
+				    OP1_MIN_RANGE() >= 0) {
+					if (ssa_op->result_def != current_var &&
+					    (ssa_op->op1_use != current_var || !last_use)) {
+						ZEND_REGSET_INCL(regset, ZREG_REG0);
+					}
+					if (sizeof(void*) == 8
+					 && !IS_SIGNED_32BIT(Z_LVAL_P(RT_CONSTANT(opline, opline->op2)) - 1)) {
+						if (!ZEND_REGSET_IN(regset, ZREG_REG0)) {
+							ZEND_REGSET_INCL(regset, ZREG_REG0);
+						} else {
+							ZEND_REGSET_INCL(regset, ZREG_REG1);
+						}
+					}
+				} else {
+					ZEND_REGSET_INCL(regset, ZREG_REG0);
+					ZEND_REGSET_INCL(regset, ZREG_REG2);
+					if (opline->op2_type == IS_CONST) {
+						ZEND_REGSET_INCL(regset, ZREG_REG1);
+					}
+				}
+			}
+			break;
+		case ZEND_IS_SMALLER:
+		case ZEND_IS_SMALLER_OR_EQUAL:
+		case ZEND_IS_EQUAL:
+		case ZEND_IS_NOT_EQUAL:
+		case ZEND_IS_IDENTICAL:
+		case ZEND_IS_NOT_IDENTICAL:
+		case ZEND_CASE:
+			op1_info = OP1_INFO();
+			op2_info = OP2_INFO();
+			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
+			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
+				regset = ZEND_REGSET_EMPTY;
+				if (!(opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ))) {
+					ZEND_REGSET_INCL(regset, ZREG_REG0);
+				}
+				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) &&
+				    opline->op1_type != IS_CONST && opline->op2_type != IS_CONST) {
+					if (ssa_op->op1_use != current_var &&
+					    ssa_op->op2_use != current_var) {
+						ZEND_REGSET_INCL(regset, ZREG_REG0);
+					}
+				}
+				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
+					ZEND_REGSET_INCL(regset, ZREG_FPR0);
+				}
+				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
+					ZEND_REGSET_INCL(regset, ZREG_FPR0);
+				}
+				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
+					if (ssa_op->op1_use != current_var &&
+					    ssa_op->op2_use != current_var) {
+						ZEND_REGSET_INCL(regset, ZREG_FPR0);
+					}
+				}
+				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
+				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
+					ZEND_REGSET_INCL(regset, ZREG_REG0);
+				}
+			}
+			break;
+		case ZEND_BOOL:
+		case ZEND_BOOL_NOT:
+		case ZEND_JMPZ:
+		case ZEND_JMPNZ:
+		case ZEND_JMPZNZ:
+		case ZEND_JMPZ_EX:
+		case ZEND_JMPNZ_EX:
+			op1_info = OP1_INFO();
+			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)))) {
+				regset = ZEND_REGSET_EMPTY;
+				if (op1_info & MAY_BE_DOUBLE) {
+					ZEND_REGSET_INCL(regset, ZREG_FPR0);
+				}
+				if (opline->opcode == ZEND_BOOL ||
+				    opline->opcode == ZEND_BOOL_NOT ||
+				    opline->opcode == ZEND_JMPZ_EX ||
+				    opline->opcode == ZEND_JMPNZ_EX) {
+					ZEND_REGSET_INCL(regset, ZREG_REG0);
+				}
+			}
+			break;
+		case ZEND_DO_UCALL:
+		case ZEND_DO_FCALL:
+		case ZEND_DO_FCALL_BY_NAME:
+		case ZEND_INCLUDE_OR_EVAL:
+		case ZEND_GENERATOR_CREATE:
+		case ZEND_YIELD:
+		case ZEND_YIELD_FROM:
+			regset = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP);
+			break;
+		default:
+			break;
+	}
+
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+		if (ssa_op == ssa->ops
+		 && JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].op == ZEND_JIT_TRACE_INIT_CALL
+		 && (JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
+			ZEND_REGSET_INCL(regset, ZREG_REG0);
+			ZEND_REGSET_INCL(regset, ZREG_REG1);
+		}
+	}
+
+	/* %r0 is used to check EG(vm_interrupt) */
+	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
+		if (ssa_op == ssa->ops
+		 && (JIT_G(current_trace)->stop == ZEND_JIT_TRACE_STOP_LOOP ||
+			 JIT_G(current_trace)->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL)) {
+#if ZTS
+			ZEND_REGSET_INCL(regset, ZREG_REG0);
+#else
+			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(vm_interrupt)))) {
+				ZEND_REGSET_INCL(regset, ZREG_REG0);
+			}
+#endif
+		}
+	} else  {
+		uint32_t b = ssa->cfg.map[ssa_op - ssa->ops];
+
+		if ((ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) != 0
+		 && ssa->cfg.blocks[b].start == ssa_op - ssa->ops) {
+#if ZTS
+			ZEND_REGSET_INCL(regset, ZREG_REG0);
+#else
+			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(vm_interrupt)))) {
+				ZEND_REGSET_INCL(regset, ZREG_REG0);
+			}
+#endif
+		}
+	}
+
+	return regset;
+}
+
+static size_t dasm_venners_size = 0;
+void **dasm_labels_veneers = NULL;
+
+static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int *b, uint32_t *cp, ptrdiff_t offset)
+{
+	void *veneer;
+	ptrdiff_t na;
+	int n, m;
+
+	/* try to reuse veneers for global labels */
+	if ((ins >> 16) == DASM_REL_LG
+	 && *(b-1) < 0
+	 && dasm_labels_veneers[-*(b-1)]) {
+
+		veneer = dasm_labels_veneers[-*(b-1)];
+		na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4;
+		n = (int)na;
+
+		/* check if we can jump to veneer */
+		if ((ptrdiff_t)n != na) {
+			/* pass */
+		} else if (!(ins & 0xf800)) {  /* B, BL */
+			if ((n & 3) == 0 && ((n+0x08000000) >> 28) == 0) {
+				return n;
+			}
+		} else if ((ins & 0x800)) {  /* B.cond, CBZ, CBNZ, LDR* literal */
+			if ((n & 3) == 0 && ((n+0x00100000) >> 21) == 0) {
+				return n;
+			}
+		} else if ((ins & 0x3000) == 0x2000) {  /* ADR */
+			/* pass */
+		} else if ((ins & 0x3000) == 0x3000) {  /* ADRP */
+			/* pass */
+		} else if ((ins & 0x1000)) {  /* TBZ, TBNZ */
+			if ((n & 3) == 0 && ((n+0x00008000) >> 16) == 0) {
+				return n;
+			}
+		}
+	}
+
+	veneer = (char*)buffer + (Dst->codesize + dasm_venners_size);
+
+	if (veneer > dasm_end) {
+		return 0; /* jit_buffer_size overflow */
+	}
+
+	na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4;
+	n = (int)na;
+
+	/* check if we can jump to veneer */
+	if ((ptrdiff_t)n != na) {
+		ZEND_ASSERT(0);
+		return 0;
+	} else if (!(ins & 0xf800)) {  /* B, BL */
+		if ((n & 3) != 0 || ((n+0x08000000) >> 28) != 0) {
+			ZEND_ASSERT(0);
+			return 0;
+		}
+	} else if ((ins & 0x800)) {  /* B.cond, CBZ, CBNZ, LDR* literal */
+		if ((n & 3) != 0 || ((n+0x00100000) >> 21) != 0) {
+			ZEND_ASSERT(0);
+			return 0;
+		}
+	} else if ((ins & 0x3000) == 0x2000) {  /* ADR */
+		ZEND_ASSERT(0);
+		return 0;
+	} else if ((ins & 0x3000) == 0x3000) {  /* ADRP */
+		ZEND_ASSERT(0);
+		return 0;
+	} else if ((ins & 0x1000)) {  /* TBZ, TBNZ */
+		if ((n & 3) != 0 || ((n+0x00008000) >> 16) != 0) {
+			ZEND_ASSERT(0);
+			return 0;
+		}
+	} else if ((ins & 0x8000)) {  /* absolute */
+		ZEND_ASSERT(0);
+		return 0;
+	} else {
+		ZEND_ASSERT(0);
+		return 0;
+	}
+
+	// TODO: support for long veneers (above 128MB) ???
+
+	/* check if we can use B to jump from veneer */
+	na = (ptrdiff_t)cp + offset - (ptrdiff_t)veneer - 4;
+	m = (int)na;
+	if ((ptrdiff_t)m != na) {
+		ZEND_ASSERT(0);
+		return 0;
+	} else if ((m & 3) != 0 || ((m+0x08000000) >> 28) != 0) {
+		ZEND_ASSERT(0);
+		return 0;
+	}
+
+	/* generate B instruction */
+	*(uint32_t*)veneer = 0x14000000 | ((m >> 2) & 0x03ffffff);
+	dasm_venners_size += 4;
+
+	if ((ins >> 16) == DASM_REL_LG
+	 && *(b-1) < 0) {
+		/* reuse this veneer for the future jumps to global label */
+		dasm_labels_veneers[-*(b-1)] = veneer;
+		/* Dst->globals[*(b-1)] = veneer; */
+
+#ifdef HAVE_DISASM
+	    if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM) {
+			const char *name = zend_jit_disasm_find_symbol((ptrdiff_t)cp + offset - 4, &offset);
+
+			if (name && !offset) {
+				if (strstr(name, "@veneer") == NULL) {
+					char *new_name;
+
+					zend_spprintf(&new_name, 0, "%s@veneer", name);
+					zend_jit_disasm_add_symbol(new_name, (uint64_t)(uintptr_t)veneer, 4);
+					efree(new_name);
+				} else {
+					zend_jit_disasm_add_symbol(name, (uint64_t)(uintptr_t)veneer, 4);
+				}
+			}
+		}
+#endif
+	}
+
+	return n;
+}
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/jit/zend_jit_arm64.h b/ext/opcache/jit/zend_jit_arm64.h
new file mode 100644
index 0000000000000..173df223e8f18
--- /dev/null
+++ b/ext/opcache/jit/zend_jit_arm64.h
@@ -0,0 +1,157 @@
+/*
+   +----------------------------------------------------------------------+
+   | Zend JIT                                                             |
+   +----------------------------------------------------------------------+
+   | Copyright (c) The PHP Group                                          |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Dmitry Stogov <dmitry@php.net>                              |
+   |          Hao Sun <hao.sun@arm.com>                                   |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef HAVE_JIT_ARM64_H
+#define HAVE_JIT_ARM64_H
+
+typedef enum _zend_reg {
+	ZREG_NONE = -1,
+
+	ZREG_X0,
+	ZREG_X1,
+	ZREG_X2,
+	ZREG_X3,
+	ZREG_X4,
+	ZREG_X5,
+	ZREG_X6,
+	ZREG_X7,
+	ZREG_X8,
+	ZREG_X9,
+	ZREG_X10,
+	ZREG_X11,
+	ZREG_X12,
+	ZREG_X13,
+	ZREG_X14,
+	ZREG_X15,
+	ZREG_X16,
+	ZREG_X17,
+	ZREG_X18,
+	ZREG_X19,
+	ZREG_X20,
+	ZREG_X21,
+	ZREG_X22,
+	ZREG_X23,
+	ZREG_X24,
+	ZREG_X25,
+	ZREG_X26,
+	ZREG_X27,
+	ZREG_X28,
+	ZREG_X29,
+	ZREG_X30,
+	ZREG_X31,
+
+	ZREG_V0,
+	ZREG_V1,
+	ZREG_V2,
+	ZREG_V3,
+	ZREG_V4,
+	ZREG_V5,
+	ZREG_V6,
+	ZREG_V7,
+	ZREG_V8,
+	ZREG_V9,
+	ZREG_V10,
+	ZREG_V11,
+	ZREG_V12,
+	ZREG_V13,
+	ZREG_V14,
+	ZREG_V15,
+	ZREG_V16,
+	ZREG_V17,
+	ZREG_V18,
+	ZREG_V19,
+	ZREG_V20,
+	ZREG_V21,
+	ZREG_V22,
+	ZREG_V23,
+	ZREG_V24,
+	ZREG_V25,
+	ZREG_V26,
+	ZREG_V27,
+	ZREG_V28,
+	ZREG_V29,
+	ZREG_V30,
+	ZREG_V31,
+
+	ZREG_NUM,
+
+	ZREG_THIS, /* used for delayed FETCH_THIS deoptimization */
+
+	/* pseudo constants used by deoptimizer */
+	ZREG_LONG_MIN_MINUS_1,
+	ZREG_LONG_MIN,
+	ZREG_LONG_MAX,
+	ZREG_LONG_MAX_PLUS_1,
+	ZREG_NULL,
+
+	ZREG_ZVAL_TRY_ADDREF,
+	ZREG_ZVAL_COPY_GPR0,
+} zend_reg;
+
+typedef struct _zend_jit_registers_buf {
+	uint64_t gpr[32];   /* general purpose integer register */
+	double   fpr[32];   /* floating point registers */
+} zend_jit_registers_buf;
+
+#define ZREG_RSP   ZREG_X31
+#define ZREG_RLR   ZREG_X30
+#define ZREG_RFP   ZREG_X29
+#define ZREG_RPR   ZREG_X18
+
+#define ZREG_FP    ZREG_X27
+#define ZREG_IP    ZREG_X28
+#define ZREG_RX    ZREG_IP
+
+#define ZREG_REG0  ZREG_X8
+#define ZREG_REG1  ZREG_X9
+#define ZREG_REG2  ZREG_X10
+
+#define ZREG_FPR0  ZREG_V0
+#define ZREG_FPR1  ZREG_V1
+
+#define ZREG_TMP1  ZREG_X15
+#define ZREG_TMP2  ZREG_X16
+#define ZREG_TMP3  ZREG_X17
+#define ZREG_FPTMP ZREG_V16
+
+#define ZREG_COPY      ZREG_REG0
+#define ZREG_FIRST_FPR ZREG_V0
+
+typedef uint64_t zend_regset;
+
+#define ZEND_REGSET_64BIT 1
+
+# define ZEND_REGSET_FIXED \
+	(ZEND_REGSET(ZREG_RSP) | ZEND_REGSET(ZREG_RLR) | ZEND_REGSET(ZREG_RFP) | \
+	ZEND_REGSET(ZREG_RPR) | ZEND_REGSET(ZREG_FP) | ZEND_REGSET(ZREG_IP) | \
+	ZEND_REGSET_INTERVAL(ZREG_TMP1, ZREG_TMP3) | ZEND_REGSET(ZREG_FPTMP))
+# define ZEND_REGSET_GP \
+	ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_X0, ZREG_X30), ZEND_REGSET_FIXED)
+# define ZEND_REGSET_FP \
+	ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_V0, ZREG_V31), ZEND_REGSET_FIXED)
+# define ZEND_REGSET_SCRATCH \
+	(ZEND_REGSET_INTERVAL(ZREG_X0, ZREG_X17) | ZEND_REGSET_INTERVAL(ZREG_V0, ZREG_V7) | \
+	ZEND_REGSET_INTERVAL(ZREG_V16, ZREG_V31))
+# define ZEND_REGSET_PRESERVED \
+	(ZEND_REGSET_INTERVAL(ZREG_X19, ZREG_X28) | ZEND_REGSET_INTERVAL(ZREG_V8, ZREG_V15))
+
+#define ZEND_REGSET_LOW_PRIORITY \
+	(ZEND_REGSET(ZREG_REG0) | ZEND_REGSET(ZREG_REG1) | ZEND_REGSET(ZREG_FPR0) | ZEND_REGSET(ZREG_FPR1))
+
+#endif /* ZEND_JIT_ARM64_H */
diff --git a/ext/opcache/jit/zend_jit_disasm.c b/ext/opcache/jit/zend_jit_disasm.c
index 451b511793926..8bbbea6e74b0f 100644
--- a/ext/opcache/jit/zend_jit_disasm.c
+++ b/ext/opcache/jit/zend_jit_disasm.c
@@ -225,6 +225,7 @@ static uint64_t zend_jit_disasm_branch_target(csh cs, const cs_insn *insn)
 {
 	unsigned int i;
 
+#if defined(__x86_64__) || defined(i386) || defined(ZEND_WIN32)
 	if (cs_insn_group(cs, insn, X86_GRP_JUMP)) {
 		for (i = 0; i < insn->detail->x86.op_count; i++) {
 			if (insn->detail->x86.operands[i].type == X86_OP_IMM) {
@@ -232,6 +233,16 @@ static uint64_t zend_jit_disasm_branch_target(csh cs, const cs_insn *insn)
 			}
 		}
 	}
+#elif defined(__aarch64__)
+	if (cs_insn_group(cs, insn, ARM64_GRP_JUMP)
+	 || insn->id == ARM64_INS_BL
+	 || insn->id == ARM64_INS_ADR) {
+		for (i = 0; i < insn->detail->arm64.op_count; i++) {
+			if (insn->detail->arm64.operands[i].type == ARM64_OP_IMM)
+				return insn->detail->arm64.operands[i].imm;
+		}
+	}
+#endif
 
 	return 0;
 }
@@ -310,7 +321,7 @@ static int zend_jit_disasm(const char    *name,
 #endif
 
 #ifdef HAVE_CAPSTONE
-# if defined(__x86_64__) || defined(_WIN64)
+# if defined(__x86_64__) || defined(_WIN64) || defined(ZEND_WIN32)
 	if (cs_open(CS_ARCH_X86, CS_MODE_64, &cs) != CS_ERR_OK)
 		return 0;
 	cs_option(cs, CS_OPT_DETAIL, CS_OPT_ON);
@@ -319,6 +330,11 @@ static int zend_jit_disasm(const char    *name,
 #  else
 	cs_option(cs, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
 #  endif
+# elif defined(__aarch64__)
+	if (cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &cs) != CS_ERR_OK)
+		return 0;
+	cs_option(cs, CS_OPT_DETAIL, CS_OPT_ON);
+	cs_option(cs, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
 # else
 	if (cs_open(CS_ARCH_X86, CS_MODE_32, &cs) != CS_ERR_OK)
 		return 0;
@@ -431,9 +447,15 @@ static int zend_jit_disasm(const char    *name,
 		}
 
 # ifdef HAVE_CAPSTONE_ITER
+		if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM_ADDR) {
+			fprintf(stderr, "    %" PRIx64 ":", insn->address);
+		}
 		fprintf(stderr, "\t%s ", insn->mnemonic);
 		p = insn->op_str;
 # else
+		if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM_ADDR) {
+			fprintf(stderr, "    %" PRIx64 ":", insn[i].address);
+		}
 		fprintf(stderr, "\t%s ", insn[i].mnemonic);
 		p = insn[i].op_str;
 # endif
@@ -533,6 +555,9 @@ static int zend_jit_disasm(const char    *name,
 				}
 			}
 		}
+		if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM_ADDR) {
+			fprintf(stderr, "    %" PRIx64 ":", ud_insn_off(&ud));
+		}
 		fprintf(stderr, "\t%s\n", ud_insn_asm(&ud));
 	}
 #endif
diff --git a/ext/opcache/jit/zend_jit_gdb.c b/ext/opcache/jit/zend_jit_gdb.c
index 0717ee32364bb..eb91dda317e56 100644
--- a/ext/opcache/jit/zend_jit_gdb.c
+++ b/ext/opcache/jit/zend_jit_gdb.c
@@ -20,9 +20,8 @@
    +----------------------------------------------------------------------+
 */
 
-#define HAVE_GDB
 
-#ifdef HAVE_GDB
+#define HAVE_GDB
 
 #include "zend_elf.h"
 #include "zend_gdb.h"
@@ -92,7 +91,9 @@ enum {
 	DW_REG_8, DW_REG_9, DW_REG_10, DW_REG_11,
 	DW_REG_12, DW_REG_13, DW_REG_14, DW_REG_15,
 	DW_REG_RA,
-	/* TODO: ARM supports? */
+#elif defined(__aarch64__)
+	DW_REG_SP = 31,
+	DW_REG_RA = 30,
 #else
 #error "Unsupported target architecture"
 #endif
@@ -160,6 +161,8 @@ static const zend_elf_header zend_elfhdr_template = {
 	.machine     = 3,
 #elif defined(__x86_64__)
 	.machine     = 62,
+#elif defined(__aarch64__)
+	.machine     = 183,
 #else
 # error "Unsupported target architecture"
 #endif
@@ -278,21 +281,26 @@ static void zend_gdbjit_symtab(zend_gdbjit_ctx *ctx)
 	sym->info = ELFSYM_INFO(ELFSYM_BIND_GLOBAL, ELFSYM_TYPE_FUNC);
 }
 
+typedef ZEND_SET_ALIGNED(1, uint16_t unaligned_uint16_t);
+typedef ZEND_SET_ALIGNED(1, uint32_t unaligned_uint32_t);
+typedef ZEND_SET_ALIGNED(1, uint16_t unaligned_uint16_t);
+typedef ZEND_SET_ALIGNED(1, uintptr_t unaligned_uintptr_t);
+
 #define SECTALIGN(p, a) \
 	  ((p) = (uint8_t *)(((uintptr_t)(p) + ((a)-1)) & ~(uintptr_t)((a)-1)))
 
 /* Shortcuts to generate DWARF structures. */
 #define DB(x)       (*p++ = (x))
 #define DI8(x)      (*(int8_t *)p = (x), p++)
-#define DU16(x)     (*(uint16_t *)p = (x), p += 2)
-#define DU32(x)     (*(uint32_t *)p = (x), p += 4)
-#define DADDR(x)    (*(uintptr_t *)p = (x), p += sizeof(uintptr_t))
+#define DU16(x)     (*(unaligned_uint16_t *)p = (x), p += 2)
+#define DU32(x)     (*(unaligned_uint32_t *)p = (x), p += 4)
+#define DADDR(x)    (*(unaligned_uintptr_t *)p = (x), p += sizeof(uintptr_t))
 #define DUV(x)      (ctx->p = p, zend_gdbjit_uleb128(ctx, (x)), p = ctx->p)
 #define DSV(x)      (ctx->p = p, zend_gdbjit_sleb128(ctx, (x)), p = ctx->p)
 #define DSTR(str)   (ctx->p = p, zend_gdbjit_strz(ctx, (str)), p = ctx->p)
 #define DALIGNNOP(s)    while ((uintptr_t)p & ((s)-1)) *p++ = DW_CFA_nop
 #define DSECT(name, stmt) \
-	{ uint32_t *szp_##name = (uint32_t *)p; p += 4; stmt \
+	{ unaligned_uint32_t *szp_##name = (uint32_t *)p; p += 4; stmt \
 		*szp_##name = (uint32_t)((p-(uint8_t *)szp_##name)-4); }
 
 static void zend_gdbjit_ehframe(zend_gdbjit_ctx *ctx)
@@ -327,6 +335,9 @@ static void zend_gdbjit_ehframe(zend_gdbjit_ctx *ctx)
 #elif defined(__x86_64__)
 		DB(DW_CFA_advance_loc|4);            /* sub $0x8,%rsp */
 		DB(DW_CFA_def_cfa_offset); DUV(16);  /* Aligned stack frame size. */
+#elif defined(__aarch64__)
+		DB(DW_CFA_advance_loc|1);            /* Only an approximation. */
+		DB(DW_CFA_def_cfa_offset); DUV(32);  /* Aligned stack frame size. */
 #else
 # error "Unsupported target architecture"
 #endif
@@ -491,5 +502,3 @@ static void zend_jit_gdb_init(void)
 	}
 #endif
 }
-
-#endif
diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h
index 83bf939e5eed6..9beb453b309c9 100644
--- a/ext/opcache/jit/zend_jit_internal.h
+++ b/ext/opcache/jit/zend_jit_internal.h
@@ -14,23 +14,39 @@
    +----------------------------------------------------------------------+
    | Authors: Dmitry Stogov <dmitry@php.net>                              |
    |          Xinchen Hui <laruence@php.net>                              |
+   |          Hao Sun <hao.sun@arm.com>                                   |
    +----------------------------------------------------------------------+
 */
 
 #ifndef ZEND_JIT_INTERNAL_H
 #define ZEND_JIT_INTERNAL_H
 
+#include "zend_bitset.h"
+
 /* Register Set */
 #define ZEND_REGSET_EMPTY 0
 
 #define ZEND_REGSET_IS_EMPTY(regset) \
 	(regset == ZEND_REGSET_EMPTY)
 
+#define ZEND_REGSET_IS_SINGLETON(regset) \
+	(regset && !(regset & (regset - 1)))
+
+#if (!ZEND_REGSET_64BIT)
 #define ZEND_REGSET(reg) \
 	(1u << (reg))
+#else
+#define ZEND_REGSET(reg) \
+	(1ull << (reg))
+#endif
 
+#if (!ZEND_REGSET_64BIT)
 #define ZEND_REGSET_INTERVAL(reg1, reg2) \
 	(((1u << ((reg2) - (reg1) + 1)) - 1) << (reg1))
+#else
+#define ZEND_REGSET_INTERVAL(reg1, reg2) \
+	(((1ull << ((reg2) - (reg1) + 1)) - 1) << (reg1))
+#endif
 
 #define ZEND_REGSET_IN(regset, reg) \
 	(((regset) & ZEND_REGSET(reg)) != 0)
@@ -50,15 +66,13 @@
 #define ZEND_REGSET_DIFFERENCE(set1, set2) \
 	((set1) & ~(set2))
 
-#ifndef _WIN32
-# if (ZREG_NUM <= 32)
+#if !defined(_WIN32)
+# if (!ZEND_REGSET_64BIT)
 #  define ZEND_REGSET_FIRST(set) ((zend_reg)__builtin_ctz(set))
 #  define ZEND_REGSET_LAST(set)  ((zend_reg)(__builtin_clz(set)^31))
-# elif(ZREG_NUM <= 64)
+# else
 #  define ZEND_REGSET_FIRST(set) ((zend_reg)__builtin_ctzll(set))
 #  define ZEND_REGSET_LAST(set)  ((zend_reg)(__builtin_clzll(set)^63))
-# else
-#  errir "Too many registers"
 # endif
 #else
 # include <intrin.h>
@@ -77,7 +91,7 @@ uint32_t __inline __zend_jit_clz(uint32_t value) {
 	return 32;
 }
 # define ZEND_REGSET_FIRST(set) ((zend_reg)__zend_jit_ctz(set))
-# define ZEND_REGSET_LAST(set)  ((zend_reg)(__zend_jit_clz(set)^31)))
+# define ZEND_REGSET_LAST(set)  ((zend_reg)(__zend_jit_clz(set)^31))
 #endif
 
 #define ZEND_REGSET_FOREACH(set, reg) \
@@ -711,4 +725,72 @@ static zend_always_inline bool zend_jit_may_be_polymorphic_call(const zend_op *o
 	}
 }
 
+/* Instruction cache flush */
+#ifndef JIT_CACHE_FLUSH
+#  if defined (__aarch64__)
+#    if ((defined(__GNUC__) && ZEND_GCC_VERSION >= 4003) || __has_builtin(__builtin___clear_cache))
+#      define JIT_CACHE_FLUSH(from, to) __builtin___clear_cache((char*)(from), (char*)(to))
+#    else
+#      error "Missing builtin to flush instruction cache for AArch64"
+#    endif
+#  else /* Not required to implement on archs with unified caches */
+#    define JIT_CACHE_FLUSH(from, to)
+#  endif
+#endif /* !JIT_CACHE_FLUSH */
+
+/* bit helpers */
+
+static zend_always_inline bool zend_long_is_power_of_two(zend_long x)
+{
+	return (x > 0) && !(x & (x - 1));
+}
+
+static zend_always_inline uint32_t zend_long_floor_log2(zend_long x)
+{
+	ZEND_ASSERT(zend_long_is_power_of_two(x));
+	return zend_ulong_ntz(x);
+}
+
+/* from http://aggregate.org/MAGIC/ */
+static zend_always_inline uint32_t ones32(uint32_t x)
+{
+	x -= ((x >> 1) & 0x55555555);
+	x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
+	x = (((x >> 4) + x) & 0x0f0f0f0f);
+	x += (x >> 8);
+	x += (x >> 16);
+	return x & 0x0000003f;
+}
+
+static zend_always_inline uint32_t floor_log2(uint32_t x)
+{
+	ZEND_ASSERT(x != 0);
+	x |= (x >> 1);
+	x |= (x >> 2);
+	x |= (x >> 4);
+	x |= (x >> 8);
+	x |= (x >> 16);
+	return ones32(x) - 1;
+}
+
+static zend_always_inline bool is_power_of_two(uint32_t x)
+{
+	return !(x & (x - 1)) && x != 0;
+}
+
+static zend_always_inline bool has_concrete_type(uint32_t value_type)
+{
+	return is_power_of_two (value_type & (MAY_BE_ANY|MAY_BE_UNDEF));
+}
+
+static zend_always_inline uint32_t concrete_type(uint32_t value_type)
+{
+	return floor_log2(value_type & (MAY_BE_ANY|MAY_BE_UNDEF));
+}
+
+static zend_always_inline bool is_signed(double d)
+{
+	return (((unsigned char*)&d)[sizeof(double)-1] & 0x80) != 0;
+}
+
 #endif /* ZEND_JIT_INTERNAL_H */
diff --git a/ext/opcache/jit/zend_jit_perf_dump.c b/ext/opcache/jit/zend_jit_perf_dump.c
index d8f2d6130e8cc..ace998fe9d9ad 100644
--- a/ext/opcache/jit/zend_jit_perf_dump.c
+++ b/ext/opcache/jit/zend_jit_perf_dump.c
@@ -22,6 +22,9 @@
 #include <unistd.h>
 #include <time.h>
 #include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #if defined(__linux__)
 #include <sys/syscall.h>
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 0803605769896..2b379e2429ea1 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -6947,7 +6947,7 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
 			} else if (STACK_REG(stack, j) == ZREG_ZVAL_COPY_GPR0) {
 				fprintf(stderr, " ");
 				zend_dump_var(op_array, (j < op_array->last_var) ? IS_CV : 0, j);
-				fprintf(stderr, ":unknown(zval_copy(%s))", zend_reg_name[0]);
+				fprintf(stderr, ":unknown(zval_copy(%s))", zend_reg_name[ZREG_COPY]);
 			}
 		}
 		fprintf(stderr, "\n");
@@ -7478,7 +7478,7 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
 			} else if (STACK_REG(stack, i) == ZREG_ZVAL_TRY_ADDREF) {
 				Z_TRY_ADDREF_P(EX_VAR_NUM(i));
 			} else if (STACK_REG(stack, i) == ZREG_ZVAL_COPY_GPR0) {
-				zval *val = (zval*)regs->gpr[0];
+				zval *val = (zval*)regs->gpr[ZREG_COPY];
 
 				if (UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) {
 					/* Undefined array index or property */
@@ -7519,7 +7519,7 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
 			}
 		}
 		if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL) {
-			zend_function *func = (zend_function*)regs->gpr[0];
+			zend_function *func = (zend_function*)regs->gpr[ZREG_COPY];
 
 			if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
 				zend_string_release_ex(func->common.function_name, 0);
diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c
index f27cf39b4cf29..bb92d2b687147 100644
--- a/ext/opcache/jit/zend_jit_vm_helpers.c
+++ b/ext/opcache/jit/zend_jit_vm_helpers.c
@@ -28,7 +28,12 @@
 #include "Optimizer/zend_func_info.h"
 #include "Optimizer/zend_call_graph.h"
 #include "zend_jit.h"
+#if defined(__x86_64__) || defined(i386) || defined(ZEND_WIN32)
 #include "zend_jit_x86.h"
+#elif defined(__aarch64__)
+#include "zend_jit_arm64.h"
+#endif
+
 #include "zend_jit_internal.h"
 
 #ifdef HAVE_GCC_GLOBAL_REGS
@@ -36,9 +41,12 @@
 # if defined(__x86_64__)
 register zend_execute_data* volatile execute_data __asm__("%r14");
 register const zend_op* volatile opline __asm__("%r15");
-# else
+# elif defined(i386)
 register zend_execute_data* volatile execute_data __asm__("%esi");
 register const zend_op* volatile opline __asm__("%edi");
+# elif defined(__aarch64__)
+register zend_execute_data* volatile execute_data __asm__("x27");
+register const zend_op* volatile opline __asm__("x28");
 # endif
 # pragma GCC diagnostic warning "-Wvolatile-register-var"
 #endif
diff --git a/ext/opcache/jit/zend_jit_vtune.c b/ext/opcache/jit/zend_jit_vtune.c
index 1f71bd741eb0d..35fd3a031022d 100644
--- a/ext/opcache/jit/zend_jit_vtune.c
+++ b/ext/opcache/jit/zend_jit_vtune.c
@@ -16,6 +16,8 @@
    +----------------------------------------------------------------------+
 */
 
+#if defined(__x86_64__) || defined(i386)
+
 #define HAVE_VTUNE 1
 
 #include "jit/vtune/jitprofiling.h"
@@ -40,3 +42,5 @@ static void zend_jit_vtune_register(const char *name,
 
 	iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&jmethod);
 }
+
+#endif /* defined(__x86_64__) || defined(i386) */
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 01910e4c0b306..52efe13cd7ea0 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -1703,50 +1703,6 @@ static void zend_jit_stop_reuse_ip(void)
 	reuse_ip = 0;
 }
 
-/* bit helpers */
-
-/* from http://aggregate.org/MAGIC/ */
-static uint32_t ones32(uint32_t x)
-{
-	x -= ((x >> 1) & 0x55555555);
-	x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
-	x = (((x >> 4) + x) & 0x0f0f0f0f);
-	x += (x >> 8);
-	x += (x >> 16);
-	return x & 0x0000003f;
-}
-
-static uint32_t floor_log2(uint32_t x)
-{
-	ZEND_ASSERT(x != 0);
-	x |= (x >> 1);
-	x |= (x >> 2);
-	x |= (x >> 4);
-	x |= (x >> 8);
-	x |= (x >> 16);
-	return ones32(x) - 1;
-}
-
-static bool is_power_of_two(uint32_t x)
-{
-	return !(x & (x - 1)) && x != 0;
-}
-
-static bool has_concrete_type(uint32_t value_type)
-{
-	return is_power_of_two (value_type & (MAY_BE_ANY|MAY_BE_UNDEF));
-}
-
-static uint32_t concrete_type(uint32_t value_type)
-{
-	return floor_log2(value_type & (MAY_BE_ANY|MAY_BE_UNDEF));
-}
-
-static inline bool is_signed(double d)
-{
-	return (((unsigned char*)&d)[sizeof(double)-1] & 0x80) != 0;
-}
-
 static int zend_jit_interrupt_handler_stub(dasm_State **Dst)
 {
 	|->interrupt_handler:
@@ -4233,7 +4189,7 @@ static int zend_jit_math_long_long(dasm_State    **Dst,
 		} else {
 			result_reg = Z_REG(res_addr);
 		}
-	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
+	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr) && !may_overflow) {
 		result_reg = Z_REG(op1_addr);
 	} else if (Z_REG(res_addr) != ZREG_R0) {
 		result_reg = ZREG_R0;
@@ -4244,32 +4200,42 @@ static int zend_jit_math_long_long(dasm_State    **Dst,
 	}
 
 	if (opcode == ZEND_MUL &&
-		Z_MODE(op2_addr) == IS_CONST_ZVAL &&
-		IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(op2_addr))) &&
-		is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr)))) {
-
-		if (Z_MODE(op1_addr) == IS_REG && Z_LVAL_P(Z_ZV(op2_addr)) == 2) {
+			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
+			Z_LVAL_P(Z_ZV(op2_addr)) == 2) {
+		if (Z_MODE(op1_addr) == IS_REG && !may_overflow) {
 			|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))+Ra(Z_REG(op1_addr))]
 		} else {
 			|	GET_ZVAL_LVAL result_reg, op1_addr
-			|	shl Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
+			|	add Ra(result_reg), Ra(result_reg)
 		}
 	} else if (opcode == ZEND_MUL &&
-		Z_MODE(op1_addr) == IS_CONST_ZVAL &&
-		IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(op1_addr))) &&
-		is_power_of_two(Z_LVAL_P(Z_ZV(op1_addr)))) {
-
-		if (Z_MODE(op2_addr) == IS_REG && Z_LVAL_P(Z_ZV(op1_addr)) == 2) {
+			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
+			!may_overflow &&
+			Z_LVAL_P(Z_ZV(op2_addr)) > 0 &&
+			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr)))) {
+		|	GET_ZVAL_LVAL result_reg, op1_addr
+		|	shl Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
+	} else if (opcode == ZEND_MUL &&
+			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
+			Z_LVAL_P(Z_ZV(op1_addr)) == 2) {
+		if (Z_MODE(op2_addr) == IS_REG && !may_overflow) {
 			|	lea Ra(result_reg), [Ra(Z_REG(op2_addr))+Ra(Z_REG(op2_addr))]
 		} else {
 			|	GET_ZVAL_LVAL result_reg, op2_addr
-			|	shl Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
+			|	add Ra(result_reg), Ra(result_reg)
 		}
+	} else if (opcode == ZEND_MUL &&
+			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
+			!may_overflow &&
+			Z_LVAL_P(Z_ZV(op1_addr)) > 0 &&
+			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op1_addr)))) {
+		|	GET_ZVAL_LVAL result_reg, op2_addr
+		|	shl Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
 	} else if (opcode == ZEND_DIV &&
 			(Z_MODE(op2_addr) == IS_CONST_ZVAL &&
-			is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr))))) {
+			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr))))) {
 		|	GET_ZVAL_LVAL result_reg, op1_addr
-		|	shr Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
+		|	shr Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
 	} else if (opcode == ZEND_ADD &&
 			!may_overflow &&
 			Z_MODE(op1_addr) == IS_REG &&
diff --git a/ext/opcache/jit/zend_jit_x86.h b/ext/opcache/jit/zend_jit_x86.h
index 795569517d2b8..924db409c3289 100644
--- a/ext/opcache/jit/zend_jit_x86.h
+++ b/ext/opcache/jit/zend_jit_x86.h
@@ -88,6 +88,7 @@ typedef struct _zend_jit_registers_buf {
 } zend_jit_registers_buf;
 
 #define ZREG_FIRST_FPR ZREG_XMM0
+#define ZREG_COPY      ZREG_R0
 
 #define ZREG_RAX ZREG_R0
 #define ZREG_RCX ZREG_R1
@@ -113,6 +114,8 @@ typedef struct _zend_jit_registers_buf {
 
 typedef uint32_t zend_regset;
 
+#define ZEND_REGSET_64BIT 0
+
 #ifdef _WIN64
 # define ZEND_REGSET_FIXED \
 	(ZEND_REGSET(ZREG_RSP) | ZEND_REGSET(ZREG_R14) | ZEND_REGSET(ZREG_R15))
diff --git a/ext/opcache/tests/bug81046.phpt b/ext/opcache/tests/bug81046.phpt
new file mode 100644
index 0000000000000..f01890cb781ba
--- /dev/null
+++ b/ext/opcache/tests/bug81046.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #81046: Literal compaction merges non-equal related literals
+--FILE--
+<?php
+
+class Test {
+	static function methoD() {
+        echo "Method called\n";
+	}
+}
+
+const methoD = 1;
+var_dump(methoD);
+test::methoD();
+
+?>
+--EXPECT--
+int(1)
+Method called
diff --git a/ext/opcache/tests/jit/add_001.phpt b/ext/opcache/tests/jit/add_001.phpt
new file mode 100644
index 0000000000000..25fb1e7b4c71f
--- /dev/null
+++ b/ext/opcache/tests/jit/add_001.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT ADD: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($var) {
+  $res = $var + 1;
+  var_dump($res);
+}
+foo(1);
+?>
+--EXPECT--
+int(2)
diff --git a/ext/opcache/tests/jit/add_002.phpt b/ext/opcache/tests/jit/add_002.phpt
new file mode 100644
index 0000000000000..bce4b6b38937b
--- /dev/null
+++ b/ext/opcache/tests/jit/add_002.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT ADD: 002
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($var) {
+  $res = $var + 0x1000;
+  var_dump($res);
+}
+foo(1);
+?>
+--EXPECT--
+int(4097)
diff --git a/ext/opcache/tests/jit/add_003.phpt b/ext/opcache/tests/jit/add_003.phpt
new file mode 100644
index 0000000000000..70e814968b124
--- /dev/null
+++ b/ext/opcache/tests/jit/add_003.phpt
@@ -0,0 +1,24 @@
+--TEST--
+JIT ADD: 003
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+function foo($var) {
+  $ret = $var + 1;
+  var_dump($ret);
+}
+foo(PHP_INT_MAX);
+?>
+--EXPECT--
+float(9.223372036854776E+18)
diff --git a/ext/opcache/tests/jit/add_004.phpt b/ext/opcache/tests/jit/add_004.phpt
new file mode 100644
index 0000000000000..761ead9e8aba4
--- /dev/null
+++ b/ext/opcache/tests/jit/add_004.phpt
@@ -0,0 +1,24 @@
+--TEST--
+JIT ADD: 004
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+function foo($var) {
+  $ret = $var + 200;
+  var_dump($ret);
+}
+foo(PHP_INT_MAX);
+?>
+--EXPECT--
+float(9.223372036854776E+18)
diff --git a/ext/opcache/tests/jit/add_005.phpt b/ext/opcache/tests/jit/add_005.phpt
new file mode 100644
index 0000000000000..7958bb97d2893
--- /dev/null
+++ b/ext/opcache/tests/jit/add_005.phpt
@@ -0,0 +1,24 @@
+--TEST--
+JIT ADD: 005
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($var) {
+  $res = $var + 2;
+  var_dump($res);
+}
+foo("hello");
+?>
+--EXPECTF--
+Fatal error: Uncaught TypeError: Unsupported operand types: string + int in %s:%d
+Stack trace:
+#0 %s(%d): foo('hello')
+#1 {main}
+  thrown in %s on line %d
diff --git a/ext/opcache/tests/jit/add_006.phpt b/ext/opcache/tests/jit/add_006.phpt
new file mode 100644
index 0000000000000..f15bdf2c4c761
--- /dev/null
+++ b/ext/opcache/tests/jit/add_006.phpt
@@ -0,0 +1,26 @@
+--TEST--
+JIT ADD: 006
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($a, $b) {
+  $res = $a + $b;
+  var_dump($res);
+}
+foo(3, 5);
+foo(3.0, 5.0);
+foo(3.0, 5);
+foo(3, 5.0);
+?>
+--EXPECT--
+int(8)
+float(8)
+float(8)
+float(8)
diff --git a/ext/opcache/tests/jit/assign_037.phpt b/ext/opcache/tests/jit/assign_037.phpt
new file mode 100644
index 0000000000000..292837ebdae16
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_037.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT ASSIGN: Assign refcounted string (with result)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+$n = 2;
+$a = $b = str_repeat('a', $n);
+var_dump($a, $b);
+?>
+--EXPECT--
+string(2) "aa"
+string(2) "aa"
diff --git a/ext/opcache/tests/jit/assign_038.phpt b/ext/opcache/tests/jit/assign_038.phpt
new file mode 100644
index 0000000000000..bcbda7b02798b
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_038.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT ASSIGN: Assign constant (with result)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+opcache.optimization_level=0 ; disable optimizer to produce ASSIGN with result
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+$a = $b = "bb";
+var_dump($a, $b);
+?>
+--EXPECT--
+string(2) "bb"
+string(2) "bb"
diff --git a/ext/opcache/tests/jit/assign_039.phpt b/ext/opcache/tests/jit/assign_039.phpt
new file mode 100644
index 0000000000000..737d37a15378c
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_039.phpt
@@ -0,0 +1,18 @@
+--TEST--
+JIT ASSIGN: Assign reference IS_VAR
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+$a = $ref =& $val;
+var_dump($a);
+?>
+--EXPECT--
+NULL
diff --git a/ext/opcache/tests/jit/concat_001.phpt b/ext/opcache/tests/jit/concat_001.phpt
new file mode 100644
index 0000000000000..85d4633765d31
--- /dev/null
+++ b/ext/opcache/tests/jit/concat_001.phpt
@@ -0,0 +1,22 @@
+--TEST--
+JIT CONCAT: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($a, $b) {
+	return $a . $b;
+}
+var_dump(foo("a", "b"));
+var_dump(foo("a", 5));
+?>
+--EXPECT--
+string(2) "ab"
+string(2) "a5"
diff --git a/ext/opcache/tests/jit/hot_func_001.phpt b/ext/opcache/tests/jit/hot_func_001.phpt
new file mode 100644
index 0000000000000..6c0346d9b479d
--- /dev/null
+++ b/ext/opcache/tests/jit/hot_func_001.phpt
@@ -0,0 +1,24 @@
+--TEST--
+JIT HOT_FUNC: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+opcache.jit=tracing
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+  var_dump("hello");
+}
+foo();
+foo();
+foo();
+?>
+--EXPECT--
+string(5) "hello"
+string(5) "hello"
+string(5) "hello"
diff --git a/ext/opcache/tests/jit/hot_func_002.phpt b/ext/opcache/tests/jit/hot_func_002.phpt
new file mode 100644
index 0000000000000..3090a8cdcc285
--- /dev/null
+++ b/ext/opcache/tests/jit/hot_func_002.phpt
@@ -0,0 +1,25 @@
+--TEST--
+JIT HOT_FUNC: 002
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+opcache.jit=tracing
+opcache.jit_hot_func=2
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+  var_dump("hello");
+}
+foo();
+foo();
+foo();
+?>
+--EXPECT--
+string(5) "hello"
+string(5) "hello"
+string(5) "hello"
diff --git a/ext/opcache/tests/jit/icall_001.phpt b/ext/opcache/tests/jit/icall_001.phpt
new file mode 100644
index 0000000000000..b52dcdddee990
--- /dev/null
+++ b/ext/opcache/tests/jit/icall_001.phpt
@@ -0,0 +1,24 @@
+--TEST--
+JIT ICALL: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+var_dump(true, 0, 42, -42, 0.0, 2.0,"hello", array());
+?>
+--EXPECT--
+bool(true)
+int(0)
+int(42)
+int(-42)
+float(0)
+float(2)
+string(5) "hello"
+array(0) {
+}
diff --git a/ext/opcache/tests/jit/identical_001.phpt b/ext/opcache/tests/jit/identical_001.phpt
new file mode 100644
index 0000000000000..03b1f4ea7f068
--- /dev/null
+++ b/ext/opcache/tests/jit/identical_001.phpt
@@ -0,0 +1,31 @@
+--TEST--
+JIT IDENTICAL: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($a, $b) {
+	return $a === $b;
+}
+var_dump(foo(0, 0));
+var_dump(foo(0, 1));
+var_dump(foo(0, 0.0));
+var_dump(foo(0.0, 0.0));
+var_dump(foo(0.0, 1.0));
+var_dump(foo("ab", "ab"));
+var_dump(foo("ab", "cd"));
+?>
+--EXPECT--
+bool(true)
+bool(false)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
diff --git a/ext/opcache/tests/jit/identical_002.phpt b/ext/opcache/tests/jit/identical_002.phpt
new file mode 100644
index 0000000000000..789a3f8d36efb
--- /dev/null
+++ b/ext/opcache/tests/jit/identical_002.phpt
@@ -0,0 +1,131 @@
+--TEST--
+JIT IDENTICAL: 002 Comparison with NaN
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function t() {
+    echo "!";
+    return true;
+}
+function f() {
+    echo "!";
+    return false;
+}
+$a = 0.0;
+$b = 0.0;
+$c = 1.0;
+$d = NAN;
+var_dump($a === $b);
+var_dump($a === $c);
+var_dump($a === $d);
+var_dump($a !== $b);
+var_dump($a !== $c);
+var_dump($a !== $d);
+var_dump($a === $b ? 1 : 0);
+var_dump($a === $c ? 1 : 0);
+var_dump($a === $d ? 1 : 0);
+var_dump($a !== $b ? 1 : 0);
+var_dump($a !== $c ? 1 : 0);
+var_dump($a !== $d ? 1 : 0);
+if ($a === $b) {
+    echo "1\n";
+}
+if ($a === $c) {
+    echo "2\n";
+}
+if ($a === $d) {
+    echo "3\n";
+}
+if ($a !== $b) {
+    echo "4\n";
+}
+if ($a !== $c) {
+    echo "5\n";
+}
+if ($a !== $d) {
+    echo "6\n";
+}
+if ($a === $b) {
+} else {
+    echo "7\n";
+}
+if ($a === $c) {
+} else {
+    echo "8\n";
+}
+if ($a === $d) {
+} else {
+    echo "9\n";
+}
+if ($a !== $b) {
+} else {
+    echo "A\n";
+}
+if ($a !== $c) {
+} else {
+    echo "B\n";
+}
+if ($a !== $d) {
+} else {
+    echo "C\n";
+}
+var_dump($a === $b && t());
+var_dump($a === $c && t());
+var_dump($a === $d && t());
+var_dump($a !== $b && t());
+var_dump($a !== $c && t());
+var_dump($a !== $d && t());
+var_dump($a === $b || f());
+var_dump($a === $c || f());
+var_dump($a === $d || f());
+var_dump($a !== $b || f());
+var_dump($a !== $c || f());
+var_dump($a !== $d || f());
+$a=NAN;
+var_dump($a === $d);
+var_dump($a !== $d);
+var_dump($a === $d ? 1 : 0);
+var_dump($a !== $d ? 1 : 0);
+?>
+--EXPECT--
+bool(true)
+bool(false)
+bool(false)
+bool(false)
+bool(true)
+bool(true)
+int(1)
+int(0)
+int(0)
+int(0)
+int(1)
+int(1)
+1
+5
+6
+8
+9
+A
+!bool(true)
+bool(false)
+bool(false)
+bool(false)
+!bool(true)
+!bool(true)
+bool(true)
+!bool(false)
+!bool(false)
+!bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+int(0)
+int(1)
diff --git a/ext/opcache/tests/jit/inc_021.phpt b/ext/opcache/tests/jit/inc_021.phpt
new file mode 100644
index 0000000000000..bfc1e73f8217f
--- /dev/null
+++ b/ext/opcache/tests/jit/inc_021.phpt
@@ -0,0 +1,33 @@
+--TEST--
+JIT INC: 021
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+function inc(int|float $x) {
+    return ++$x;
+}
+function dec(int|float $x) {
+    return --$x;
+}
+var_dump(inc(PHP_INT_MAX));
+var_dump(inc(1.1));
+var_dump(dec(PHP_INT_MIN));
+var_dump(dec(1.1));
+?>
+--EXPECT--
+float(9.223372036854776E+18)
+float(2.1)
+float(-9.223372036854776E+18)
+float(0.10000000000000009)
diff --git a/ext/opcache/tests/jit/inc_022.phpt b/ext/opcache/tests/jit/inc_022.phpt
new file mode 100644
index 0000000000000..75971cff6c2e9
--- /dev/null
+++ b/ext/opcache/tests/jit/inc_022.phpt
@@ -0,0 +1,31 @@
+--TEST--
+JIT INC: 022
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function inc($x) {
+    return ++$x;
+}
+function dec($x) {
+    return --$x;
+}
+var_dump(inc("abc"));
+var_dump(inc("5"));
+var_dump(inc(1.1));
+var_dump(dec("5"));
+var_dump(dec(1.1));
+?>
+--EXPECT--
+string(3) "abd"
+int(6)
+float(2.1)
+int(4)
+float(0.10000000000000009)
diff --git a/ext/opcache/tests/jit/loop_001.phpt b/ext/opcache/tests/jit/loop_001.phpt
new file mode 100644
index 0000000000000..ed2a918b49cc1
--- /dev/null
+++ b/ext/opcache/tests/jit/loop_001.phpt
@@ -0,0 +1,22 @@
+--TEST--
+JIT LOOP: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($n) {
+  $res = 0;
+  for ($i = 1; $i < $n; $i++)
+    $res = $res + $i;
+  return $res;
+}
+print foo(5);
+?>
+--EXPECT--
+10
diff --git a/ext/opcache/tests/jit/loop_002.phpt b/ext/opcache/tests/jit/loop_002.phpt
new file mode 100644
index 0000000000000..499fd670d1881
--- /dev/null
+++ b/ext/opcache/tests/jit/loop_002.phpt
@@ -0,0 +1,26 @@
+--TEST--
+JIT HOT LOOP: 002
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+opcache.jit=tracing
+opcache.jit_hot_func=2
+opcache.jit_hot_loop=2
+opcache.jit_hot_side_exit=0
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($n) {
+  $res = 0;
+  for ($i = 1; $i < $n; $i++)
+    $res = $res + $i;
+  return $res;
+}
+print foo(5);
+?>
+--EXPECT--
+10
diff --git a/ext/opcache/tests/jit/mul_001.phpt b/ext/opcache/tests/jit/mul_001.phpt
new file mode 100644
index 0000000000000..94a07bfce4e71
--- /dev/null
+++ b/ext/opcache/tests/jit/mul_001.phpt
@@ -0,0 +1,32 @@
+--TEST--
+JIT MUL: 001 integer multiplay
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function mul2(int $a) {
+  $res = $a * 2;
+  var_dump($res);
+}
+function mul4(int $a) {
+  $res = $a * 4;
+  var_dump($res);
+}
+function mul111(int $a) {
+  $res = $a * 111;
+  var_dump($res);
+}
+mul2(3);
+mul4(3);
+mul111(3);
+?>
+--EXPECT--
+int(6)
+int(12)
+int(333)
diff --git a/ext/opcache/tests/jit/mul_002.phpt b/ext/opcache/tests/jit/mul_002.phpt
new file mode 100644
index 0000000000000..eecedc11a844a
--- /dev/null
+++ b/ext/opcache/tests/jit/mul_002.phpt
@@ -0,0 +1,24 @@
+--TEST--
+JIT MUL: 002 integer overflow
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+function mul(int $a, int $b) {
+  $res = $a * $b;
+  var_dump($res);
+}
+mul(0x5555555555, 0x5555555555);
+?>
+--EXPECT--
+float(1.343250910680478E+23)
diff --git a/ext/opcache/tests/jit/mul_003.phpt b/ext/opcache/tests/jit/mul_003.phpt
new file mode 100644
index 0000000000000..5042cd6c3acb4
--- /dev/null
+++ b/ext/opcache/tests/jit/mul_003.phpt
@@ -0,0 +1,33 @@
+--TEST--
+JIT MUL: 003 boundary value for optmizing MUL to SHIFT
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+
+function mul2_bound(int $a) {
+  $res = $a * -2147483648;
+  var_dump($res);
+}
+
+function mul1_bound(int $a) {
+  $res = -2147483648 * $a;
+  var_dump($res);
+}
+
+mul2_bound(3);
+mul1_bound(3);
+?>
+--EXPECT--
+int(-6442450944)
+int(-6442450944)
\ No newline at end of file
diff --git a/ext/opcache/tests/jit/mul_004.phpt b/ext/opcache/tests/jit/mul_004.phpt
new file mode 100644
index 0000000000000..b1855a0ee489e
--- /dev/null
+++ b/ext/opcache/tests/jit/mul_004.phpt
@@ -0,0 +1,72 @@
+--TEST--
+JIT MUL: 004 Overflow check for optmizing MUL to SHIFT
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+
+function mul2_8(int $a) {
+  $res = $a * 8;  // shift cnt: 3
+  var_dump($res);
+}
+
+function mul1_16(int $a) {
+  $res = 16 * $a; // shift cnt: 4
+  var_dump($res);
+}
+
+function mul2_big_int32(int $a) {
+  $res = $a * 0x10000000; // shift cnt: 29
+  var_dump($res);
+}
+
+function mul2_big_int64(int $a) {
+  $res = $a * 0x100000000; // shift cnt: 32
+  var_dump($res);
+}
+
+function mul2(int $a) {
+  $res = $a * 2; // $a + $a
+  var_dump($res);
+}
+
+mul2_8(3);
+mul2_8(-11);
+mul2_8(0x7fffffffffffffff);
+mul1_16(3);
+mul1_16(-13);
+mul1_16(0x7fffffffffffffff);
+mul2_big_int32(3);
+mul2_big_int32(-3);
+mul2_big_int32(0x10000000000);
+mul2_big_int64(3);
+mul2_big_int64(-3);
+mul2_big_int64(0x100000000);
+mul2(10);
+mul2(0x7fffffffffffffff);
+?>
+--EXPECT--
+int(24)
+int(-88)
+float(7.378697629483821E+19)
+int(48)
+int(-208)
+float(1.4757395258967641E+20)
+int(805306368)
+int(-805306368)
+float(2.9514790517935283E+20)
+int(12884901888)
+int(-12884901888)
+float(1.8446744073709552E+19)
+int(20)
+float(1.8446744073709552E+19)
diff --git a/ext/opcache/tests/jit/not_001.phpt b/ext/opcache/tests/jit/not_001.phpt
new file mode 100644
index 0000000000000..0820b7e942ba6
--- /dev/null
+++ b/ext/opcache/tests/jit/not_001.phpt
@@ -0,0 +1,26 @@
+--TEST--
+JIT NOT: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($x) {
+	return !$x;
+}
+var_dump(foo(1));
+var_dump(foo(0));
+var_dump(foo(1.0));
+var_dump(foo(0.0));
+?>
+--EXPECT--
+bool(false)
+bool(true)
+bool(false)
+bool(true)
diff --git a/ext/opcache/tests/jit/not_002.phpt b/ext/opcache/tests/jit/not_002.phpt
new file mode 100644
index 0000000000000..68df4ab774db2
--- /dev/null
+++ b/ext/opcache/tests/jit/not_002.phpt
@@ -0,0 +1,22 @@
+--TEST--
+JIT NOT: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo(float $x) {
+	return !$x;
+}
+var_dump(foo(1.0));
+var_dump(foo(0.0));
+?>
+--EXPECT--
+bool(false)
+bool(true)
diff --git a/ext/opcache/tests/jit/recv_002.phpt b/ext/opcache/tests/jit/recv_002.phpt
new file mode 100644
index 0000000000000..0f38edc4400eb
--- /dev/null
+++ b/ext/opcache/tests/jit/recv_002.phpt
@@ -0,0 +1,27 @@
+--TEST--
+JIT RECV: too few arguments
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+
+function test($a)
+{
+}
+
+test();
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ArgumentCountError: Too few arguments to function test(), 0 passed in %srecv_002.php on line 7 and exactly 1 expected in %s:3
+Stack trace:
+#0 %s(7): test()
+#1 {main}
+  thrown in %s on line 3
\ No newline at end of file
diff --git a/ext/opcache/tests/jit/recv_003.phpt b/ext/opcache/tests/jit/recv_003.phpt
new file mode 100644
index 0000000000000..0eb214e9cac6e
--- /dev/null
+++ b/ext/opcache/tests/jit/recv_003.phpt
@@ -0,0 +1,36 @@
+--TEST--
+JIT RECV: slow type check
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+class A {
+}
+class B extends A {
+}
+class C {
+}
+
+function test(A $x)
+{
+	echo "ok\n";
+}
+
+test(new B);
+test(new C);
+?>
+--EXPECTF--
+ok
+
+Fatal error: Uncaught TypeError: test(): Argument #1 ($x) must be of type A, C given, called in %s:9
+Stack trace:
+#0 %s(15): test(Object(C))
+#1 {main}
+  thrown in %s on line 9
diff --git a/ext/opcache/tests/jit/recv_004.phpt b/ext/opcache/tests/jit/recv_004.phpt
new file mode 100644
index 0000000000000..4e540f29cb008
--- /dev/null
+++ b/ext/opcache/tests/jit/recv_004.phpt
@@ -0,0 +1,22 @@
+--TEST--
+JIT RECV: default arguments with type checks
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+opcache.protect_memory=1
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function test(int $a = 1, int $b = 2)
+{
+	var_dump($a, $b);
+}
+test(3);
+?>
+--EXPECT--
+int(3)
+int(2)
diff --git a/ext/opcache/tests/jit/recv_005.phpt b/ext/opcache/tests/jit/recv_005.phpt
new file mode 100644
index 0000000000000..239cbc938cf03
--- /dev/null
+++ b/ext/opcache/tests/jit/recv_005.phpt
@@ -0,0 +1,26 @@
+--TEST--
+JIT RECV: 005
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($var) {
+  var_dump($var);
+}
+foo(1);
+foo(1.0);
+foo("hello");
+foo(array());
+?>
+--EXPECT--
+int(1)
+float(1)
+string(5) "hello"
+array(0) {
+}
diff --git a/ext/opcache/tests/jit/ret_001.phpt b/ext/opcache/tests/jit/ret_001.phpt
new file mode 100644
index 0000000000000..59408c72a4b78
--- /dev/null
+++ b/ext/opcache/tests/jit/ret_001.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT RET: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+  $c = 1;
+  return $c;
+}
+var_dump(foo());
+?>
+--EXPECT--
+int(1)
diff --git a/ext/opcache/tests/jit/ret_002.phpt b/ext/opcache/tests/jit/ret_002.phpt
new file mode 100644
index 0000000000000..a755bd78a45af
--- /dev/null
+++ b/ext/opcache/tests/jit/ret_002.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT RET: 002
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+  $c = 1.0;
+  return $c;
+}
+var_dump(foo());
+?>
+--EXPECT--
+float(1)
diff --git a/ext/opcache/tests/jit/ret_003.phpt b/ext/opcache/tests/jit/ret_003.phpt
new file mode 100644
index 0000000000000..1bc716b9e5bee
--- /dev/null
+++ b/ext/opcache/tests/jit/ret_003.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT RET: 003
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+  $c = "hello";
+  return $c;
+}
+var_dump(foo());
+?>
+--EXPECT--
+string(5) "hello"
diff --git a/ext/opcache/tests/jit/sub_001.phpt b/ext/opcache/tests/jit/sub_001.phpt
new file mode 100644
index 0000000000000..4c98c14738868
--- /dev/null
+++ b/ext/opcache/tests/jit/sub_001.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT SUB: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($var) {
+  $res = $var - 1;
+  var_dump($res);
+}
+foo(42);
+?>
+--EXPECT--
+int(41)
diff --git a/ext/opcache/tests/jit/ucall_001.phpt b/ext/opcache/tests/jit/ucall_001.phpt
new file mode 100644
index 0000000000000..df69fc7018d6e
--- /dev/null
+++ b/ext/opcache/tests/jit/ucall_001.phpt
@@ -0,0 +1,19 @@
+--TEST--
+JIT UCALL: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+    var_dump("hello");
+}
+foo();
+?>
+--EXPECT--
+string(5) "hello"
diff --git a/ext/opcache/tests/jit/ucall_002.phpt b/ext/opcache/tests/jit/ucall_002.phpt
new file mode 100644
index 0000000000000..14787e7737501
--- /dev/null
+++ b/ext/opcache/tests/jit/ucall_002.phpt
@@ -0,0 +1,21 @@
+--TEST--
+JIT UCALL: 002
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+    var_dump("hello");
+}
+foo();
+var_dump("world!");
+?>
+--EXPECT--
+string(5) "hello"
+string(6) "world!"
diff --git a/ext/opcache/tests/jit/ucall_003.phpt b/ext/opcache/tests/jit/ucall_003.phpt
new file mode 100644
index 0000000000000..89b0776c9b55d
--- /dev/null
+++ b/ext/opcache/tests/jit/ucall_003.phpt
@@ -0,0 +1,22 @@
+--TEST--
+JIT UCALL: 003
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+  var_dump("hello");
+}
+function bar() {
+  foo();
+}
+bar();
+?>
+--EXPECT--
+string(5) "hello"
diff --git a/ext/opcache/tests/jit/ucall_004.phpt b/ext/opcache/tests/jit/ucall_004.phpt
new file mode 100644
index 0000000000000..0f29e4d22f1d5
--- /dev/null
+++ b/ext/opcache/tests/jit/ucall_004.phpt
@@ -0,0 +1,23 @@
+--TEST--
+JIT UCALL: 004
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo() {
+  var_dump("hello");
+}
+foo();
+foo();
+foo();
+?>
+--EXPECT--
+string(5) "hello"
+string(5) "hello"
+string(5) "hello"
diff --git a/ext/opcache/tests/jit/xor_001.phpt b/ext/opcache/tests/jit/xor_001.phpt
new file mode 100644
index 0000000000000..abae4cd1beee4
--- /dev/null
+++ b/ext/opcache/tests/jit/xor_001.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT XOR: 001
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($var) {
+  $res = $var ^ 1;
+  var_dump($res);
+}
+foo(5);
+?>
+--EXPECT--
+int(4)
diff --git a/ext/opcache/tests/jit/xor_002.phpt b/ext/opcache/tests/jit/xor_002.phpt
new file mode 100644
index 0000000000000..2f8ff8461300c
--- /dev/null
+++ b/ext/opcache/tests/jit/xor_002.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT XOR: 002
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($var) {
+  $res = $var ^ $var;
+  var_dump($res);
+}
+foo(5);
+?>
+--EXPECT--
+int(0)
diff --git a/ext/opcache/tests/jit/xor_003.phpt b/ext/opcache/tests/jit/xor_003.phpt
new file mode 100644
index 0000000000000..9d0277b4b3583
--- /dev/null
+++ b/ext/opcache/tests/jit/xor_003.phpt
@@ -0,0 +1,20 @@
+--TEST--
+JIT XOR: 003
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=32M
+;opcache.jit_debug=257
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function foo($a, $b) {
+  $res = $a ^ $b;
+  var_dump($res);
+}
+foo("abc", "\001\002\003");
+?>
+--EXPECT--
+string(3) "```"
diff --git a/ext/openssl/config0.m4 b/ext/openssl/config0.m4
index e08a76897aeeb..ffd4e0751cc6b 100644
--- a/ext/openssl/config0.m4
+++ b/ext/openssl/config0.m4
@@ -28,14 +28,15 @@ if test "$PHP_OPENSSL" != "no"; then
     PHP_EVAL_LIBLINE($KERBEROS_LIBS, OPENSSL_SHARED_LIBADD)
   fi
 
-  AC_CHECK_FUNCS([RAND_egd])
-
   PHP_SETUP_OPENSSL(OPENSSL_SHARED_LIBADD,
   [
     AC_DEFINE(HAVE_OPENSSL_EXT,1,[ ])
   ], [
     AC_MSG_ERROR([OpenSSL check failed. Please check config.log for more information.])
   ])
+
+  AC_CHECK_FUNCS([RAND_egd])
+
   if test "$PHP_SYSTEM_CIPHERS" != "no"; then
     AC_DEFINE(USE_OPENSSL_SYSTEM_CIPHERS,1,[ Use system default cipher list instead of hardcoded value ])
   fi
diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c
index 4708e643b26be..4249980e5586e 100644
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@ -1500,8 +1500,8 @@ PHP_FUNCTION(preg_match_all)
 /* {{{ preg_get_backref */
 static int preg_get_backref(char **str, int *backref)
 {
-	register char in_brace = 0;
-	register char *walk = *str;
+	char in_brace = 0;
+	char *walk = *str;
 
 	if (walk[1] == 0)
 		return 0;
diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c
index c5dcc264adb1a..c12e48139690e 100644
--- a/ext/pdo_mysql/mysql_driver.c
+++ b/ext/pdo_mysql/mysql_driver.c
@@ -94,7 +94,11 @@ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int lin
 				dbh->is_persistent);
 
 		} else {
-			einfo->errmsg = pestrdup(mysql_error(H->server), dbh->is_persistent);
+			if (S && S->stmt) {
+				einfo->errmsg = pestrdup(mysql_stmt_error(S->stmt), dbh->is_persistent);
+			} else {
+				einfo->errmsg = pestrdup(mysql_error(H->server), dbh->is_persistent);
+			}
 		}
 	} else { /* no error */
 		strcpy(*pdo_err, PDO_ERR_NONE);
diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c
index 3f08980d54c6a..401031e86b42e 100644
--- a/ext/pdo_mysql/mysql_statement.c
+++ b/ext/pdo_mysql/mysql_statement.c
@@ -315,6 +315,15 @@ static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
 	S->done = 0;
 
 	if (S->stmt) {
+		uint32_t num_bound_params =
+			stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0;
+		if (num_bound_params < (uint32_t) S->num_params) {
+			/* too few parameter bound */
+			PDO_DBG_ERR("too few parameters bound");
+			strcpy(stmt->error_code, "HY093");
+			PDO_DBG_RETURN(0);
+		}
+
 		PDO_DBG_RETURN(pdo_mysql_stmt_execute_prepared(stmt));
 	}
 
@@ -403,13 +412,6 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da
 				PDO_DBG_RETURN(1);
 
 			case PDO_PARAM_EVT_EXEC_PRE:
-				if (zend_hash_num_elements(stmt->bound_params) < (unsigned int) S->num_params) {
-					/* too few parameter bound */
-					PDO_DBG_ERR("too few parameters bound");
-					strcpy(stmt->error_code, "HY093");
-					PDO_DBG_RETURN(0);
-				}
-
 				if (!Z_ISREF(param->parameter)) {
 					parameter = &param->parameter;
 				} else {
diff --git a/ext/pdo_mysql/tests/bug79596.phpt b/ext/pdo_mysql/tests/bug79596.phpt
index 185b2e60b14f8..b71bbd5d8c954 100644
--- a/ext/pdo_mysql/tests/bug79596.phpt
+++ b/ext/pdo_mysql/tests/bug79596.phpt
@@ -6,6 +6,7 @@ require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
 require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
 MySQLPDOTest::skip();
 if (!setlocale(LC_ALL, 'de_DE', 'de-DE')) die('skip German locale not available');
+if (!MySQLPDOTest::isPDOMySQLnd()) die('skip libmysql returns result as string');
 ?>
 --FILE--
 <?php
diff --git a/ext/pdo_mysql/tests/bug81037.phpt b/ext/pdo_mysql/tests/bug81037.phpt
new file mode 100644
index 0000000000000..1452839652033
--- /dev/null
+++ b/ext/pdo_mysql/tests/bug81037.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Bug #81037 PDO discards error message text from prepared statement
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
+
+$pdo = MySQLPDOTest::factory();
+$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
+MySQLPDOTest::createTestTable($pdo);
+
+$sql = "SELECT id FROM test WHERE label = :par";
+$stmt = $pdo->prepare($sql);
+try {
+    $stmt->execute();
+} catch (PDOException $e) {
+    echo $e->getMessage(), "\n";
+}
+$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+?>
+--CLEAN--
+<?php
+require __DIR__ . '/mysql_pdo_test.inc';
+MySQLPDOTest::dropTestTable();
+?>
+--EXPECT--
+SQLSTATE[HY093]: Invalid parameter number
diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt
index 683c6e0bbee07..a2df9b4597c52 100644
--- a/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_unbuffered_2050.phpt
@@ -138,11 +138,11 @@ array(1) {
 }
 Unbuffered...
 
-Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: 2050  in %s on line %d
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: 2050 Row retrieval was canceled by mysql_stmt_close() call in %s on line %d
 array(0) {
 }
 
-Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: 2050  in %s on line %d
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: 2050 Row retrieval was canceled by mysql_stmt_close() call in %s on line %d
 array(0) {
 }
 array(1) {
diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c
index 6ebe8407093bf..806ba55840f86 100644
--- a/ext/pdo_pgsql/pgsql_driver.c
+++ b/ext/pdo_pgsql/pgsql_driver.c
@@ -37,7 +37,7 @@
 
 static char * _pdo_pgsql_trim_message(const char *message, int persistent)
 {
-	register int i = strlen(message)-1;
+	size_t i = strlen(message)-1;
 	char *tmp;
 
 	if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index ae601bf92d441..c012ac763b841 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -885,22 +885,16 @@ PHP_METHOD(Phar, mungServer)
 			RETURN_THROWS();
 		}
 
-		if (Z_STRLEN_P(data) == sizeof("PHP_SELF")-1 && !strncmp(Z_STRVAL_P(data), "PHP_SELF", sizeof("PHP_SELF")-1)) {
+		if (zend_string_equals_literal(Z_STR_P(data), "PHP_SELF")) {
 			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_PHP_SELF;
-		}
-
-		if (Z_STRLEN_P(data) == sizeof("REQUEST_URI")-1) {
-			if (!strncmp(Z_STRVAL_P(data), "REQUEST_URI", sizeof("REQUEST_URI")-1)) {
-				PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_REQUEST_URI;
-			}
-			if (!strncmp(Z_STRVAL_P(data), "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1)) {
-				PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_SCRIPT_NAME;
-			}
-		}
-
-		if (Z_STRLEN_P(data) == sizeof("SCRIPT_FILENAME")-1 && !strncmp(Z_STRVAL_P(data), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1)) {
+		} else if (zend_string_equals_literal(Z_STR_P(data), "REQUEST_URI")) {
+			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_REQUEST_URI;
+		} else if (zend_string_equals_literal(Z_STR_P(data), "SCRIPT_NAME")) {
+			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_SCRIPT_NAME;
+		} else if (zend_string_equals_literal(Z_STR_P(data), "SCRIPT_FILENAME")) {
 			PHAR_G(phar_SERVER_mung_list) |= PHAR_MUNG_SCRIPT_FILENAME;
 		}
+		// TODO Warning for invalid value?
 	} ZEND_HASH_FOREACH_END();
 }
 /* }}} */
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 4b05a4d0ac569..020c56d6cf5d7 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -849,8 +849,8 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent
 	}
 	_function_parameter_string(str, fptr, ZSTR_VAL(param_indent.s));
 	smart_str_free(&param_indent);
-	if (fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
-		smart_str_append_printf(str, "  %s- Return [ ", indent);
+	if ((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+		smart_str_append_printf(str, "  %s- %s [ ", indent, ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]) ? "Tentative return" : "Return");
 		if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) {
 			zend_string *type_str = zend_type_to_string(fptr->common.arg_info[-1].type);
 			smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str));
@@ -3449,7 +3449,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasReturnType)
 
 	GET_REFLECTION_OBJECT_PTR(fptr);
 
-	RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE);
+	RETVAL_BOOL((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]));
 }
 /* }}} */
 
@@ -3465,7 +3465,43 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType)
 
 	GET_REFLECTION_OBJECT_PTR(fptr);
 
-	if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+	if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) {
+		RETURN_NULL();
+	}
+
+	reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1);
+}
+/* }}} */
+
+/* {{{ Return whether the function has a return type */
+ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType)
+{
+	reflection_object *intern;
+	zend_function *fptr;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		RETURN_THROWS();
+	}
+
+	GET_REFLECTION_OBJECT_PTR(fptr);
+
+	RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]));
+}
+/* }}} */
+
+/* {{{ Returns the return type associated with the function */
+ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType)
+{
+	reflection_object *intern;
+	zend_function *fptr;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		RETURN_THROWS();
+	}
+
+	GET_REFLECTION_OBJECT_PTR(fptr);
+
+	if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) {
 		RETURN_NULL();
 	}
 
diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php
index 69384f88bb320..7bd22b6d839d9 100644
--- a/ext/reflection/php_reflection.stub.php
+++ b/ext/reflection/php_reflection.stub.php
@@ -98,6 +98,10 @@ public function hasReturnType() {}
     /** @return ReflectionType|null */
     public function getReturnType() {}
 
+    public function hasTentativeReturnType(): bool {}
+
+    public function getTentativeReturnType(): ?ReflectionType {}
+
     /** @return ReflectionAttribute[] */
     public function getAttributes(?string $name = null, int $flags = 0): array {}
 }
diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h
index b1ea070d2ac3a..fa48db957e96c 100644
--- a/ext/reflection/php_reflection_arginfo.h
+++ b/ext/reflection/php_reflection_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 47ac64b027cdeb0e9996147277f79fa9d6b876bd */
+ * Stub hash: 0b2b52d4f891a594ccfcbcc0edeec97a9e0f80e6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
@@ -59,6 +59,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionFunctionAbstract_getReturnType arginfo_class_ReflectionFunctionAbstract_inNamespace
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType, 0, 0, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType, 0, 0, ReflectionType, 1)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, name, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
@@ -218,8 +224,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionClass_isTrait arginfo_class_ReflectionFunctionAbstract_inNamespace
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionClass_isEnum, 0, 0, _IS_BOOL, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_ReflectionClass_isEnum arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
 
 #define arginfo_class_ReflectionClass_isAbstract arginfo_class_ReflectionFunctionAbstract_inNamespace
 
@@ -311,7 +316,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionProperty_isDefault arginfo_class_ReflectionFunctionAbstract_inNamespace
 
-#define arginfo_class_ReflectionProperty_isPromoted arginfo_class_ReflectionClass_isEnum
+#define arginfo_class_ReflectionProperty_isPromoted arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
 
 #define arginfo_class_ReflectionProperty_getModifiers arginfo_class_ReflectionFunctionAbstract_inNamespace
 
@@ -325,7 +330,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionProperty_hasType arginfo_class_ReflectionFunctionAbstract_inNamespace
 
-#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionClass_isEnum
+#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
 
 #define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_ReflectionFunctionAbstract_inNamespace
 
@@ -358,7 +363,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionClassConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes
 
-#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionClass_isEnum
+#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
 
 #define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone
 
@@ -405,7 +410,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_ReflectionFunctionAbstract_inNamespace
 
-#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionClass_isEnum
+#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
 
 #define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes
 
@@ -482,7 +487,7 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_getTarget, 0, 0, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionClass_isEnum
+#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
 
 #define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionUnionType_getTypes
 
@@ -505,10 +510,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionEnum_getCases arginfo_class_ReflectionUnionType_getTypes
 
-#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionClass_isEnum
+#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnum_getBackingType, 0, 0, ReflectionType, 1)
-ZEND_END_ARG_INFO()
+#define arginfo_class_ReflectionEnum_getBackingType arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType
 
 #define arginfo_class_ReflectionEnumUnitCase___construct arginfo_class_ReflectionClassConstant___construct
 
@@ -569,6 +573,8 @@ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables);
 ZEND_METHOD(ReflectionFunctionAbstract, returnsReference);
 ZEND_METHOD(ReflectionFunctionAbstract, hasReturnType);
 ZEND_METHOD(ReflectionFunctionAbstract, getReturnType);
+ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType);
+ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType);
 ZEND_METHOD(ReflectionFunctionAbstract, getAttributes);
 ZEND_METHOD(ReflectionFunction, __construct);
 ZEND_METHOD(ReflectionFunction, __toString);
@@ -805,6 +811,8 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = {
 	ZEND_ME(ReflectionFunctionAbstract, returnsReference, arginfo_class_ReflectionFunctionAbstract_returnsReference, ZEND_ACC_PUBLIC)
 	ZEND_ME(ReflectionFunctionAbstract, hasReturnType, arginfo_class_ReflectionFunctionAbstract_hasReturnType, ZEND_ACC_PUBLIC)
 	ZEND_ME(ReflectionFunctionAbstract, getReturnType, arginfo_class_ReflectionFunctionAbstract_getReturnType, ZEND_ACC_PUBLIC)
+	ZEND_ME(ReflectionFunctionAbstract, hasTentativeReturnType, arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType, ZEND_ACC_PUBLIC)
+	ZEND_ME(ReflectionFunctionAbstract, getTentativeReturnType, arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType, ZEND_ACC_PUBLIC)
 	ZEND_ME(ReflectionFunctionAbstract, getAttributes, arginfo_class_ReflectionFunctionAbstract_getAttributes, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/ext/reflection/tests/ReflectionEnumUnitCase_getDocComment.phpt b/ext/reflection/tests/ReflectionEnumUnitCase_getDocComment.phpt
new file mode 100644
index 0000000000000..d51543601d917
--- /dev/null
+++ b/ext/reflection/tests/ReflectionEnumUnitCase_getDocComment.phpt
@@ -0,0 +1,23 @@
+--TEST--
+ReflectionEnumUnitCase::getDocComment()
+--FILE--
+<?php
+// enum cases should support doc comments, like class constants.
+
+enum Foo {
+    /** Example doc comment */
+    case Bar;
+    case Baz;
+}
+
+var_dump((new ReflectionEnumUnitCase(Foo::class, 'Bar'))->getDocComment());
+var_dump((new ReflectionEnumUnitCase(Foo::class, 'Baz'))->getDocComment());
+var_dump((new ReflectionClassConstant(Foo::class, 'Bar'))->getDocComment());
+var_dump((new ReflectionClassConstant(Foo::class, 'Baz'))->getDocComment());
+
+?>
+--EXPECT--
+string(26) "/** Example doc comment */"
+bool(false)
+string(26) "/** Example doc comment */"
+bool(false)
diff --git a/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt b/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt
new file mode 100644
index 0000000000000..576b87172292a
--- /dev/null
+++ b/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt
@@ -0,0 +1,62 @@
+--TEST--
+ReflectionMethod returns tentative return type information correctly
+--FILE--
+<?php
+
+class MyDateTimeZone extends DateTimeZone
+{
+    #[ReturnTypeWillChange]
+    public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string
+    {
+        return "";
+    }
+}
+
+$methodInfo = new ReflectionMethod(DateTimeZone::class, 'listIdentifiers');
+
+var_dump($methodInfo->hasReturnType());
+var_dump($methodInfo->hasTentativeReturnType());
+var_dump($methodInfo->getReturnType());
+var_dump((string) $methodInfo->getTentativeReturnType());
+var_dump((string) $methodInfo);
+echo "\n";
+
+$methodInfo = new ReflectionMethod(MyDateTimeZone::class, 'listIdentifiers');
+
+var_dump($methodInfo->hasReturnType());
+var_dump($methodInfo->hasTentativeReturnType());
+var_dump((string) $methodInfo->getReturnType());
+var_dump($methodInfo->getTentativeReturnType());
+var_dump((string) $methodInfo);
+echo "\n";
+
+?>
+--EXPECTF--
+bool(false)
+bool(true)
+NULL
+string(5) "array"
+string(%d) "Method [ <internal:date> static public method listIdentifiers ] {
+
+  - Parameters [2] {
+    Parameter #0 [ <optional> int $timezoneGroup = DateTimeZone::ALL ]
+    Parameter #1 [ <optional> ?string $countryCode = null ]
+  }
+  - Tentative return [ array ]
+}
+"
+
+bool(true)
+bool(false)
+string(6) "string"
+NULL
+string(%d) "Method [ <user, overwrites DateTimeZone, prototype DateTimeZone> static public method listIdentifiers ] {
+  @@ %s
+
+  - Parameters [2] {
+    Parameter #0 [ <optional> int $timezoneGroup = %d ]
+    Parameter #1 [ <optional> ?string $countryCode = NULL ]
+  }
+  - Return [ string ]
+}
+"
diff --git a/ext/reflection/tests/bug62715.phpt b/ext/reflection/tests/bug62715.phpt
index 63339cddc1ec1..b0080b6da63b4 100644
--- a/ext/reflection/tests/bug62715.phpt
+++ b/ext/reflection/tests/bug62715.phpt
@@ -17,9 +17,7 @@ foreach ($r->getParameters() as $p) {
 }
 ?>
 --EXPECTF--
-Deprecated: Required parameter $c follows optional parameter $b in %s on line %d
-bool(true)
-bool(true)
+Deprecated: Optional parameter $b declared before required parameter $c is implicitly treated as a required parameter in %s on line %d
+bool(false)
+bool(false)
 bool(false)
-NULL
-int(0)
diff --git a/ext/session/tests/rfc1867.phpt b/ext/session/tests/rfc1867.phpt
index a5ae8b22181f7..aed179d757333 100644
--- a/ext/session/tests/rfc1867.phpt
+++ b/ext/session/tests/rfc1867.phpt
@@ -54,9 +54,11 @@ string(%d) "rfc1867"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -67,9 +69,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_cleanup.phpt b/ext/session/tests/rfc1867_cleanup.phpt
index 9baa6144ba688..9a0bf626f33ad 100644
--- a/ext/session/tests/rfc1867_cleanup.phpt
+++ b/ext/session/tests/rfc1867_cleanup.phpt
@@ -54,9 +54,11 @@ string(%d) "rfc1867-cleanup"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -67,9 +69,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_disabled.phpt b/ext/session/tests/rfc1867_disabled.phpt
index 8d8effd1f42ca..43c1079064d65 100644
--- a/ext/session/tests/rfc1867_disabled.phpt
+++ b/ext/session/tests/rfc1867_disabled.phpt
@@ -47,9 +47,11 @@ session_destroy();
 string(%d) "rfc1867-disabled"
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -60,9 +62,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_disabled_2.phpt b/ext/session/tests/rfc1867_disabled_2.phpt
index c539c6eaeae4e..f2ff6ebb96424 100644
--- a/ext/session/tests/rfc1867_disabled_2.phpt
+++ b/ext/session/tests/rfc1867_disabled_2.phpt
@@ -47,9 +47,11 @@ session_destroy();
 string(%d) "rfc1867-disabled-2"
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -60,9 +62,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_inter.phpt b/ext/session/tests/rfc1867_inter.phpt
index fd28dfe07a30c..db0ca00a05dcc 100644
--- a/ext/session/tests/rfc1867_inter.phpt
+++ b/ext/session/tests/rfc1867_inter.phpt
@@ -57,9 +57,11 @@ session_destroy();
 string(%d) "rfc1867-inter"
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -70,9 +72,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_no_name.phpt b/ext/session/tests/rfc1867_no_name.phpt
index 15877a664e644..b27120dc5bb33 100644
--- a/ext/session/tests/rfc1867_no_name.phpt
+++ b/ext/session/tests/rfc1867_no_name.phpt
@@ -47,9 +47,11 @@ session_destroy();
 string(%d) "rfc1867-no-name"
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -60,9 +62,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_sid_cookie.phpt b/ext/session/tests/rfc1867_sid_cookie.phpt
index 85c28934f440b..9acd8d68f808b 100644
--- a/ext/session/tests/rfc1867_sid_cookie.phpt
+++ b/ext/session/tests/rfc1867_sid_cookie.phpt
@@ -53,9 +53,11 @@ string(%d) "rfc1867-sid-cookie"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -66,9 +68,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_sid_get.phpt b/ext/session/tests/rfc1867_sid_get.phpt
index dfb192cb47cb0..b9dde7bb2ed01 100644
--- a/ext/session/tests/rfc1867_sid_get.phpt
+++ b/ext/session/tests/rfc1867_sid_get.phpt
@@ -51,9 +51,11 @@ string(%d) "rfc1867-sid-get"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -64,9 +66,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_sid_get_2.phpt b/ext/session/tests/rfc1867_sid_get_2.phpt
index 33e4489cc8bd8..4f0f598a8a517 100644
--- a/ext/session/tests/rfc1867_sid_get_2.phpt
+++ b/ext/session/tests/rfc1867_sid_get_2.phpt
@@ -53,9 +53,11 @@ string(%d) "rfc1867-sid-get-2"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -66,9 +68,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_sid_invalid.phpt b/ext/session/tests/rfc1867_sid_invalid.phpt
index 4d8372c538149..23e3bdcd37cf7 100644
--- a/ext/session/tests/rfc1867_sid_invalid.phpt
+++ b/ext/session/tests/rfc1867_sid_invalid.phpt
@@ -65,9 +65,11 @@ string(%d) ""
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -78,9 +80,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_sid_only_cookie.phpt b/ext/session/tests/rfc1867_sid_only_cookie.phpt
index 54897b91c8582..d438068c8f661 100644
--- a/ext/session/tests/rfc1867_sid_only_cookie.phpt
+++ b/ext/session/tests/rfc1867_sid_only_cookie.phpt
@@ -53,9 +53,11 @@ string(%d) "rfc1867-sid-only-cookie"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -66,9 +68,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_sid_only_cookie_2.phpt b/ext/session/tests/rfc1867_sid_only_cookie_2.phpt
index 3fd46148d724e..698fc1609d36e 100644
--- a/ext/session/tests/rfc1867_sid_only_cookie_2.phpt
+++ b/ext/session/tests/rfc1867_sid_only_cookie_2.phpt
@@ -50,9 +50,11 @@ string(%d) "%s"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -63,9 +65,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/session/tests/rfc1867_sid_post.phpt b/ext/session/tests/rfc1867_sid_post.phpt
index f22f1534039dc..4ab3e6c33efb8 100644
--- a/ext/session/tests/rfc1867_sid_post.phpt
+++ b/ext/session/tests/rfc1867_sid_post.phpt
@@ -49,9 +49,11 @@ string(%d) "rfc1867-sid-post"
 bool(true)
 array(2) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -62,9 +64,11 @@ array(2) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index ac19efb1ac45a..9b5305a509f26 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -43,11 +43,6 @@ PHP_SXE_API zend_class_entry *sxe_get_element_class_entry(void) /* {{{ */
 }
 /* }}} */
 
-#define SXE_ME(func, arg_info, flags) PHP_ME(simplexml_element, func, arg_info, flags)
-#define SXE_MALIAS(func, alias, arg_info, flags) PHP_MALIAS(simplexml_element, func, alias, arg_info, flags)
-
-#define SXE_METHOD(func) PHP_METHOD(SimpleXMLElement, func)
-
 static php_sxe_object* php_sxe_object_new(zend_class_entry *ce, zend_function *fptr_count);
 static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data);
 static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, int use_data);
@@ -1265,7 +1260,7 @@ static int sxe_objects_compare(zval *object1, zval *object2) /* {{{ */
 /* }}} */
 
 /* {{{ Runs XPath query on the XML data */
-SXE_METHOD(xpath)
+PHP_METHOD(SimpleXMLElement, xpath)
 {
 	php_sxe_object    *sxe;
 	zval               value;
@@ -1353,7 +1348,7 @@ SXE_METHOD(xpath)
 /* }}} */
 
 /* {{{ Creates a prefix/ns context for the next XPath query */
-SXE_METHOD(registerXPathNamespace)
+PHP_METHOD(SimpleXMLElement, registerXPathNamespace)
 {
 	php_sxe_object    *sxe;
 	size_t prefix_len, ns_uri_len;
@@ -1382,7 +1377,7 @@ SXE_METHOD(registerXPathNamespace)
 /* }}} */
 
 /* {{{ Return a well-formed XML string based on SimpleXML element */
-SXE_METHOD(asXML)
+PHP_METHOD(SimpleXMLElement, asXML)
 {
 	php_sxe_object     *sxe;
 	xmlNodePtr          node;
@@ -1507,7 +1502,7 @@ static void sxe_add_namespaces(php_sxe_object *sxe, xmlNodePtr node, bool recurs
 } /* }}} */
 
 /* {{{ Return all namespaces in use */
-SXE_METHOD(getNamespaces)
+PHP_METHOD(SimpleXMLElement, getNamespaces)
 {
 	bool           recursive = 0;
 	php_sxe_object     *sxe;
@@ -1555,7 +1550,7 @@ static void sxe_add_registered_namespaces(php_sxe_object *sxe, xmlNodePtr node,
 /* }}} */
 
 /* {{{ Return all namespaces registered with document */
-SXE_METHOD(getDocNamespaces)
+PHP_METHOD(SimpleXMLElement, getDocNamespaces)
 {
 	bool           recursive = 0, from_root = 1;
 	php_sxe_object     *sxe;
@@ -1587,7 +1582,7 @@ SXE_METHOD(getDocNamespaces)
 /* }}} */
 
 /* {{{ Finds children of given node */
-SXE_METHOD(children)
+PHP_METHOD(SimpleXMLElement, children)
 {
 	php_sxe_object *sxe;
 	char           *nsprefix = NULL;
@@ -1617,7 +1612,7 @@ SXE_METHOD(children)
 /* }}} */
 
 /* {{{ Finds children of given node */
-SXE_METHOD(getName)
+PHP_METHOD(SimpleXMLElement, getName)
 {
 	php_sxe_object *sxe;
 	xmlNodePtr      node;
@@ -1641,7 +1636,7 @@ SXE_METHOD(getName)
 /* }}} */
 
 /* {{{ Identifies an element's attributes */
-SXE_METHOD(attributes)
+PHP_METHOD(SimpleXMLElement, attributes)
 {
 	php_sxe_object *sxe;
 	char           *nsprefix = NULL;
@@ -1669,7 +1664,7 @@ SXE_METHOD(attributes)
 /* }}} */
 
 /* {{{ Add Element with optional namespace information */
-SXE_METHOD(addChild)
+PHP_METHOD(SimpleXMLElement, addChild)
 {
 	php_sxe_object *sxe;
 	char           *qname, *value = NULL, *nsuri = NULL;
@@ -1733,7 +1728,7 @@ SXE_METHOD(addChild)
 /* }}} */
 
 /* {{{ Add Attribute with optional namespace information */
-SXE_METHOD(addAttribute)
+PHP_METHOD(SimpleXMLElement, addAttribute)
 {
 	php_sxe_object *sxe;
 	char           *qname, *value = NULL, *nsuri = NULL;
@@ -1900,7 +1895,7 @@ static int sxe_object_cast(zend_object *readobj, zval *writeobj, int type)
 /* }}} */
 
 /* {{{ Returns the string content */
-SXE_METHOD(__toString)
+PHP_METHOD(SimpleXMLElement, __toString)
 {
 	if (zend_parse_parameters_none() == FAILURE) {
 		RETURN_THROWS();
@@ -1959,7 +1954,7 @@ static int sxe_count_elements(zend_object *object, zend_long *count) /* {{{ */
 /* }}} */
 
 /* {{{ Get number of child elements */
-SXE_METHOD(count)
+PHP_METHOD(SimpleXMLElement, count)
 {
 	zend_long count = 0;
 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
@@ -1976,7 +1971,7 @@ SXE_METHOD(count)
 
 
 /* {{{ Rewind to first element */
-SXE_METHOD(rewind)
+PHP_METHOD(SimpleXMLElement, rewind)
 {
 	if (zend_parse_parameters_none() == FAILURE) {
 		RETURN_THROWS();
@@ -1987,7 +1982,7 @@ SXE_METHOD(rewind)
 /* }}} */
 
 /* {{{ Check whether iteration is valid */
-SXE_METHOD(valid)
+PHP_METHOD(SimpleXMLElement, valid)
 {
 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
 
@@ -2000,7 +1995,7 @@ SXE_METHOD(valid)
 /* }}} */
 
 /* {{{ Get current element */
-SXE_METHOD(current)
+PHP_METHOD(SimpleXMLElement, current)
 {
 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
 	zval *data;
@@ -2019,7 +2014,7 @@ SXE_METHOD(current)
 /* }}} */
 
 /* {{{ Get name of current child element */
-SXE_METHOD(key)
+PHP_METHOD(SimpleXMLElement, key)
 {
 	xmlNodePtr curnode;
 	php_sxe_object *intern;
@@ -2044,7 +2039,7 @@ SXE_METHOD(key)
 /* }}} */
 
 /* {{{ Move to next element */
-SXE_METHOD(next)
+PHP_METHOD(SimpleXMLElement, next)
 {
 	if (zend_parse_parameters_none() == FAILURE) {
 		RETURN_THROWS();
@@ -2055,7 +2050,7 @@ SXE_METHOD(next)
 /* }}} */
 
 /* {{{ Check whether element has children (elements) */
-SXE_METHOD(hasChildren)
+PHP_METHOD(SimpleXMLElement, hasChildren)
 {
 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
 	php_sxe_object *child;
@@ -2082,7 +2077,7 @@ SXE_METHOD(hasChildren)
 /* }}} */
 
 /* {{{ Get child element iterator */
-SXE_METHOD(getChildren)
+PHP_METHOD(SimpleXMLElement, getChildren)
 {
 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
 	zval *data;
@@ -2354,7 +2349,7 @@ PHP_FUNCTION(simplexml_load_string)
 /* }}} */
 
 /* {{{ SimpleXMLElement constructor */
-SXE_METHOD(__construct)
+PHP_METHOD(SimpleXMLElement, __construct)
 {
 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
 	char           *data, *ns = NULL;
diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c
index 017c88ce51198..699e6fa97fcb8 100644
--- a/ext/snmp/snmp.c
+++ b/ext/snmp/snmp.c
@@ -1667,29 +1667,36 @@ zval *php_snmp_read_property(zend_object *object, zend_string *name, int type, v
 }
 /* }}} */
 
-/* {{{ php_snmp_write_property(zval *object, zval *member, zval *value[, const zend_literal *key])
-   Generic object property writer */
+/* {{{ Generic object property writer */
 zval *php_snmp_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
 {
-	php_snmp_object *obj;
-	php_snmp_prop_handler *hnd;
+	php_snmp_object *obj = php_snmp_fetch_object(object);
+	php_snmp_prop_handler *hnd = zend_hash_find_ptr(&php_snmp_properties, name);
 
-	obj = php_snmp_fetch_object(object);
-	hnd = zend_hash_find_ptr(&php_snmp_properties, name);
+	if (hnd) {
+		if (!hnd->write_func) {
+			zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
+			return &EG(error_zval);
+		}
 
-	if (hnd && hnd->write_func) {
-		hnd->write_func(obj, value);
-		/*
-		if (!PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) == 0) {
-			Z_ADDREF_P(value);
-			zval_ptr_dtor(&value);
+		zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true);
+		if (prop && ZEND_TYPE_IS_SET(prop->type)) {
+			zval tmp;
+			ZVAL_COPY(&tmp, value);
+			if (!zend_verify_property_type(prop, &tmp,
+						ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) {
+				zval_ptr_dtor(&tmp);
+				return &EG(error_zval);
+			}
+			hnd->write_func(obj, &tmp);
+			zval_ptr_dtor(&tmp);
+		} else {
+			hnd->write_func(obj, value);
 		}
-		*/
-	} else {
-		value = zend_std_write_property(object, name, value, cache_slot);
+		return value;
 	}
 
-	return value;
+	return zend_std_write_property(object, name, value, cache_slot);
 }
 /* }}} */
 
@@ -1762,6 +1769,16 @@ static HashTable *php_snmp_get_properties(zend_object *object)
 }
 /* }}} */
 
+static zval *php_snmp_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
+{
+	php_snmp_prop_handler *hnd = zend_hash_find_ptr(&php_snmp_properties, name);
+	if (hnd == NULL) {
+		return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
+	}
+
+	return NULL;
+}
+
 /* {{{ */
 static int php_snmp_read_info(php_snmp_object *snmp_object, zval *retval)
 {
@@ -1820,14 +1837,6 @@ PHP_SNMP_LONG_PROPERTY_READER_FUNCTION(valueretrieval)
 PHP_SNMP_LONG_PROPERTY_READER_FUNCTION(oid_output_format)
 PHP_SNMP_LONG_PROPERTY_READER_FUNCTION(exceptions_enabled)
 
-/* {{{ */
-static int php_snmp_write_info(php_snmp_object *snmp_object, zval *newval)
-{
-	zend_throw_error(NULL, "SNMP::$info property is read-only");
-	return FAILURE;
-}
-/* }}} */
-
 /* {{{ */
 static int php_snmp_write_max_oids(php_snmp_object *snmp_object, zval *newval)
 {
@@ -1841,7 +1850,7 @@ static int php_snmp_write_max_oids(php_snmp_object *snmp_object, zval *newval)
 	lval = zval_get_long(newval);
 
 	if (lval <= 0) {
-		zend_value_error("max_oids must be greater than 0 or null");
+		zend_value_error("SNMP::$max_oids must be greater than 0 or null");
 		return FAILURE;
 	}
 	snmp_object->max_oids = lval;
@@ -1924,8 +1933,11 @@ static void free_php_snmp_properties(zval *el)  /* {{{ */
 #define PHP_SNMP_PROPERTY_ENTRY_RECORD(name) \
 	{ "" #name "",		sizeof("" #name "") - 1,	php_snmp_read_##name,	php_snmp_write_##name }
 
+#define PHP_SNMP_READONLY_PROPERTY_ENTRY_RECORD(name) \
+	{ "" #name "",		sizeof("" #name "") - 1,	php_snmp_read_##name,	NULL }
+
 const php_snmp_prop_handler php_snmp_property_entries[] = {
-	PHP_SNMP_PROPERTY_ENTRY_RECORD(info),
+	PHP_SNMP_READONLY_PROPERTY_ENTRY_RECORD(info),
 	PHP_SNMP_PROPERTY_ENTRY_RECORD(max_oids),
 	PHP_SNMP_PROPERTY_ENTRY_RECORD(valueretrieval),
 	PHP_SNMP_PROPERTY_ENTRY_RECORD(quick_print),
@@ -1961,6 +1973,7 @@ PHP_MINIT_FUNCTION(snmp)
 	memcpy(&php_snmp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
 	php_snmp_object_handlers.read_property = php_snmp_read_property;
 	php_snmp_object_handlers.write_property = php_snmp_write_property;
+	php_snmp_object_handlers.get_property_ptr_ptr = php_snmp_get_property_ptr_ptr;
 	php_snmp_object_handlers.has_property = php_snmp_has_property;
 	php_snmp_object_handlers.get_properties = php_snmp_get_properties;
 	php_snmp_object_handlers.get_gc = php_snmp_get_gc;
diff --git a/ext/snmp/snmp.stub.php b/ext/snmp/snmp.stub.php
index 1378f34ff9989..db61d1c0d6a1a 100644
--- a/ext/snmp/snmp.stub.php
+++ b/ext/snmp/snmp.stub.php
@@ -75,6 +75,16 @@ function snmp_read_mib(string $filename): bool {}
 
 class SNMP
 {
+    /** @readonly */
+    public array $info;
+    public ?int $max_oids;
+    public int $valueretrieval;
+    public bool $quick_print;
+    public bool $enum_print;
+    public int $oid_output_format;
+    public bool $oid_increasing_check;
+    public int $exceptions_enabled;
+
     public function __construct(int $version, string $hostname, string $community, int $timeout = -1, int $retries = -1) {}
 
     /** @return bool */
diff --git a/ext/snmp/snmp_arginfo.h b/ext/snmp/snmp_arginfo.h
index 0f076ca2a4b72..8088755169fb9 100644
--- a/ext/snmp/snmp_arginfo.h
+++ b/ext/snmp/snmp_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 08192d87d2ac5d35092cfcf4a2cdcc50f7ec4ada */
+ * Stub hash: 5258c5796aca15e369dd72c0a8ed4dc1df31ce9d */
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_snmpget, 0, 3, stdClass, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, hostname, IS_STRING, 0)
@@ -249,6 +249,54 @@ static zend_class_entry *register_class_SNMP(void)
 	INIT_CLASS_ENTRY(ce, "SNMP", class_SNMP_methods);
 	class_entry = zend_register_internal_class_ex(&ce, NULL);
 
+	zval property_info_default_value;
+	ZVAL_UNDEF(&property_info_default_value);
+	zend_string *property_info_name = zend_string_init("info", sizeof("info") - 1, 1);
+	zend_declare_typed_property(class_entry, property_info_name, &property_info_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
+	zend_string_release(property_info_name);
+
+	zval property_max_oids_default_value;
+	ZVAL_UNDEF(&property_max_oids_default_value);
+	zend_string *property_max_oids_name = zend_string_init("max_oids", sizeof("max_oids") - 1, 1);
+	zend_declare_typed_property(class_entry, property_max_oids_name, &property_max_oids_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG|MAY_BE_NULL));
+	zend_string_release(property_max_oids_name);
+
+	zval property_valueretrieval_default_value;
+	ZVAL_UNDEF(&property_valueretrieval_default_value);
+	zend_string *property_valueretrieval_name = zend_string_init("valueretrieval", sizeof("valueretrieval") - 1, 1);
+	zend_declare_typed_property(class_entry, property_valueretrieval_name, &property_valueretrieval_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
+	zend_string_release(property_valueretrieval_name);
+
+	zval property_quick_print_default_value;
+	ZVAL_UNDEF(&property_quick_print_default_value);
+	zend_string *property_quick_print_name = zend_string_init("quick_print", sizeof("quick_print") - 1, 1);
+	zend_declare_typed_property(class_entry, property_quick_print_name, &property_quick_print_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+	zend_string_release(property_quick_print_name);
+
+	zval property_enum_print_default_value;
+	ZVAL_UNDEF(&property_enum_print_default_value);
+	zend_string *property_enum_print_name = zend_string_init("enum_print", sizeof("enum_print") - 1, 1);
+	zend_declare_typed_property(class_entry, property_enum_print_name, &property_enum_print_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+	zend_string_release(property_enum_print_name);
+
+	zval property_oid_output_format_default_value;
+	ZVAL_UNDEF(&property_oid_output_format_default_value);
+	zend_string *property_oid_output_format_name = zend_string_init("oid_output_format", sizeof("oid_output_format") - 1, 1);
+	zend_declare_typed_property(class_entry, property_oid_output_format_name, &property_oid_output_format_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
+	zend_string_release(property_oid_output_format_name);
+
+	zval property_oid_increasing_check_default_value;
+	ZVAL_UNDEF(&property_oid_increasing_check_default_value);
+	zend_string *property_oid_increasing_check_name = zend_string_init("oid_increasing_check", sizeof("oid_increasing_check") - 1, 1);
+	zend_declare_typed_property(class_entry, property_oid_increasing_check_name, &property_oid_increasing_check_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+	zend_string_release(property_oid_increasing_check_name);
+
+	zval property_exceptions_enabled_default_value;
+	ZVAL_UNDEF(&property_exceptions_enabled_default_value);
+	zend_string *property_exceptions_enabled_name = zend_string_init("exceptions_enabled", sizeof("exceptions_enabled") - 1, 1);
+	zend_declare_typed_property(class_entry, property_exceptions_enabled_name, &property_exceptions_enabled_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
+	zend_string_release(property_exceptions_enabled_name);
+
 	return class_entry;
 }
 
diff --git a/ext/snmp/tests/snmp-object-error.phpt b/ext/snmp/tests/snmp-object-error.phpt
index b170f9a08323b..77e2d6bf4f7e8 100644
--- a/ext/snmp/tests/snmp-object-error.phpt
+++ b/ext/snmp/tests/snmp-object-error.phpt
@@ -75,7 +75,7 @@ $session = new SNMP(SNMP::VERSION_2c, $hostname, $community, $timeout, $retries)
 var_dump($session->max_oids);
 try {
     $session->max_oids = "ttt";
-} catch (\ValueError $e) {
+} catch (TypeError $e) {
     echo $e->getMessage() . \PHP_EOL;
 }
 try {
@@ -103,6 +103,6 @@ Closing session
 bool(true)
 Invalid or uninitialized SNMP object
 NULL
-max_oids must be greater than 0 or null
-max_oids must be greater than 0 or null
+Cannot assign string to property SNMP::$max_oids of type ?int
+SNMP::$max_oids must be greater than 0 or null
 NULL
diff --git a/ext/snmp/tests/snmp-object-properties-error.phpt b/ext/snmp/tests/snmp-object-properties-error.phpt
new file mode 100644
index 0000000000000..4a8e8edab723d
--- /dev/null
+++ b/ext/snmp/tests/snmp-object-properties-error.phpt
@@ -0,0 +1,84 @@
+--TEST--
+Test SNMP object property errors
+--SKIPIF--
+<?php
+require_once(__DIR__.'/skipif.inc');
+?>
+--FILE--
+<?php
+require_once(__DIR__.'/snmp_include.inc');
+
+$session = new SNMP(SNMP::VERSION_1, $hostname, $community, $timeout, $retries);
+
+try {
+    $session->info = [];
+} catch (Error $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->info += [];
+} catch (Error $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->max_oids = [];
+} catch (TypeError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->max_oids = -1;
+} catch (ValueError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->valueretrieval = [];
+} catch (TypeError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->quick_print = [];
+} catch (TypeError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->enum_print = [];
+} catch (TypeError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->oid_output_format = [];
+} catch (TypeError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->oid_increasing_check = [];
+} catch (TypeError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+try {
+    $session->exceptions_enabled = [];
+} catch (TypeError $exception) {
+    echo $exception->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+Cannot write read-only property SNMP::$info
+Cannot write read-only property SNMP::$info
+Cannot assign array to property SNMP::$max_oids of type ?int
+SNMP::$max_oids must be greater than 0 or null
+Cannot assign array to property SNMP::$valueretrieval of type int
+Cannot assign array to property SNMP::$quick_print of type bool
+Cannot assign array to property SNMP::$enum_print of type bool
+Cannot assign array to property SNMP::$oid_output_format of type int
+Cannot assign array to property SNMP::$oid_increasing_check of type bool
+Cannot assign array to property SNMP::$exceptions_enabled of type int
diff --git a/ext/snmp/tests/snmp-object-properties.phpt b/ext/snmp/tests/snmp-object-properties.phpt
index 47c6dc663870c..af37eaac565df 100644
--- a/ext/snmp/tests/snmp-object-properties.phpt
+++ b/ext/snmp/tests/snmp-object-properties.phpt
@@ -192,5 +192,5 @@ NULL
 bool(false)
 SNMP retrieval method must be a bitmask of SNMP_VALUE_LIBRARY, SNMP_VALUE_PLAIN, and SNMP_VALUE_OBJECT
 SNMP output print format must be an SNMP_OID_OUTPUT_* constant
-SNMP::$info property is read-only
+Cannot write read-only property SNMP::$info
 NULL
diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index 8564e1e4dcd82..ef8b721e93b60 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -731,23 +731,19 @@ static HashTable* soap_create_typemap(sdlPtr sdl, HashTable *ht) /* {{{ */
 
 		ZEND_HASH_FOREACH_STR_KEY_VAL(ht2, name, tmp) {
 			if (name) {
-				if (ZSTR_LEN(name) == sizeof("type_name")-1 &&
-				    strncmp(ZSTR_VAL(name), "type_name", sizeof("type_name")-1) == 0) {
+				if (zend_string_equals_literal(name, "type_name")) {
 					if (Z_TYPE_P(tmp) == IS_STRING) {
 						type_name = Z_STRVAL_P(tmp);
 					} else if (Z_TYPE_P(tmp) != IS_NULL) {
 					}
-				} else if (ZSTR_LEN(name) == sizeof("type_ns")-1 &&
-				    strncmp(ZSTR_VAL(name), "type_ns", sizeof("type_ns")-1) == 0) {
+				} else if (zend_string_equals_literal(name, "type_ns")) {
 					if (Z_TYPE_P(tmp) == IS_STRING) {
 						type_ns = Z_STRVAL_P(tmp);
 					} else if (Z_TYPE_P(tmp) != IS_NULL) {
 					}
-				} else if (ZSTR_LEN(name) == sizeof("to_xml")-1 &&
-				    strncmp(ZSTR_VAL(name), "to_xml", sizeof("to_xml")-1) == 0) {
+				} else if (zend_string_equals_literal(name, "to_xml")) {
 					to_xml = tmp;
-				} else if (ZSTR_LEN(name) == sizeof("from_xml")-1 &&
-				    strncmp(ZSTR_VAL(name), "from_xml", sizeof("from_xml")-1) == 0) {
+				} else if (zend_string_equals_literal(name, "from_xml")) {
 					to_zval = tmp;
 				}
 			}
@@ -1756,7 +1752,7 @@ static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeade
 	if ((Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY || zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER))) &&
 		(agent_name = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_USER_AGENT", sizeof("HTTP_USER_AGENT")-1)) != NULL &&
 		Z_TYPE_P(agent_name) == IS_STRING) {
-		if (strncmp(Z_STRVAL_P(agent_name), "Shockwave Flash", sizeof("Shockwave Flash")-1) == 0) {
+		if (zend_string_equals_literal(Z_STR_P(agent_name), "Shockwave Flash")) {
 			use_http_error_status = 0;
 		}
 	}
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 1e63b16289770..0dfcdaf33c3c3 100755
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -2246,12 +2246,13 @@ PHP_FUNCTION(ignore_user_abort)
 /* {{{ Returns port associated with service. Protocol must be "tcp" or "udp" */
 PHP_FUNCTION(getservbyname)
 {
-	char *name, *proto;
-	size_t name_len, proto_len;
+	zend_string *name;
+	char *proto;
+	size_t proto_len;
 	struct servent *serv;
 
 	ZEND_PARSE_PARAMETERS_START(2, 2)
-		Z_PARAM_STRING(name, name_len)
+		Z_PARAM_STR(name)
 		Z_PARAM_STRING(proto, proto_len)
 	ZEND_PARSE_PARAMETERS_END();
 
@@ -2264,14 +2265,14 @@ PHP_FUNCTION(getservbyname)
 	}
 #endif
 
-	serv = getservbyname(name, proto);
+	serv = getservbyname(ZSTR_VAL(name), proto);
 
 #if defined(_AIX)
 	/*
         On AIX, imap is only known as imap2 in /etc/services, while on Linux imap is an alias for imap2.
         If a request for imap gives no result, we try again with imap2.
         */
-	if (serv == NULL && strcmp(name,  "imap") == 0) {
+	if (serv == NULL && zend_string_equals_literal(name, "imap")) {
 		serv = getservbyname("imap2", proto);
 	}
 #endif
diff --git a/ext/standard/exec.c b/ext/standard/exec.c
index d5e4542caad05..1831b8eaa54d7 100644
--- a/ext/standard/exec.c
+++ b/ext/standard/exec.c
@@ -281,7 +281,7 @@ PHP_FUNCTION(passthru)
 */
 PHPAPI zend_string *php_escape_shell_cmd(const char *str)
 {
-	register size_t x, y;
+	size_t x, y;
 	size_t l = strlen(str);
 	uint64_t estimate = (2 * (uint64_t)l) + 1;
 	zend_string *cmd;
diff --git a/ext/standard/file.c b/ext/standard/file.c
index 3010cd860931b..1afa4c648f114 100644
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@ -722,7 +722,7 @@ PHP_FUNCTION(file)
 	char *filename;
 	size_t filename_len;
 	char *p, *s, *e;
-	register int i = 0;
+	int i = 0;
 	char eol_marker = '\n';
 	zend_long flags = 0;
 	bool use_include_path;
diff --git a/ext/standard/flock_compat.c b/ext/standard/flock_compat.c
index 47511379d1f0f..34a790b6b8484 100644
--- a/ext/standard/flock_compat.c
+++ b/ext/standard/flock_compat.c
@@ -162,10 +162,10 @@ PHPAPI int php_flock(int fd, int operation)
 int inet_aton(const char *cp, struct in_addr *ap)
 {
     int dots = 0;
-    register unsigned long acc = 0, addr = 0;
+    unsigned long acc = 0, addr = 0;
 
     do {
-        register char cc = *cp;
+        char cc = *cp;
 
         switch (cc) {
         case '0':
diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c
index e500a95734520..0990b390d6b29 100644
--- a/ext/standard/formatted_print.c
+++ b/ext/standard/formatted_print.c
@@ -85,7 +85,7 @@ php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add,
 						   size_t min_width, size_t max_width, char padding,
 						   size_t alignment, size_t len, int neg, int expprec, int always_sign)
 {
-	register size_t npad;
+	size_t npad;
 	size_t req_size;
 	size_t copy_len;
 	size_t m_width;
@@ -143,8 +143,8 @@ php_sprintf_appendint(zend_string **buffer, size_t *pos, zend_long number,
 						int always_sign)
 {
 	char numbuf[NUM_BUF_SIZE];
-	register zend_ulong magn, nmagn;
-	register unsigned int i = NUM_BUF_SIZE - 1, neg = 0;
+	zend_ulong magn, nmagn;
+	unsigned int i = NUM_BUF_SIZE - 1, neg = 0;
 
 	PRINTF_DEBUG(("sprintf: appendint(%x, %x, %x, %d, %d, '%c', %d)\n",
 				  *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment));
@@ -187,8 +187,8 @@ php_sprintf_appenduint(zend_string **buffer, size_t *pos,
 					   size_t width, char padding, size_t alignment)
 {
 	char numbuf[NUM_BUF_SIZE];
-	register zend_ulong magn, nmagn;
-	register unsigned int i = NUM_BUF_SIZE - 1;
+	zend_ulong magn, nmagn;
+	unsigned int i = NUM_BUF_SIZE - 1;
 
 	PRINTF_DEBUG(("sprintf: appenduint(%x, %x, %x, %d, %d, '%c', %d)\n",
 				  *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment));
@@ -326,9 +326,9 @@ php_sprintf_append2n(zend_string **buffer, size_t *pos, zend_long number,
 					 const char *chartable, int expprec)
 {
 	char numbuf[NUM_BUF_SIZE];
-	register zend_ulong num;
-	register zend_ulong  i = NUM_BUF_SIZE - 1;
-	register int andbits = (1 << n) - 1;
+	zend_ulong num;
+	zend_ulong  i = NUM_BUF_SIZE - 1;
+	int andbits = (1 << n) - 1;
 
 	PRINTF_DEBUG(("sprintf: append2n(%x, %x, %x, %d, %d, '%c', %d, %d, %x)\n",
 				  *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, n,
@@ -355,8 +355,8 @@ inline static int
 php_sprintf_getnumber(char **buffer, size_t *len)
 {
 	char *endptr;
-	register zend_long num = ZEND_STRTOL(*buffer, &endptr, 10);
-	register size_t i;
+	zend_long num = ZEND_STRTOL(*buffer, &endptr, 10);
+	size_t i;
 
 	if (endptr != NULL) {
 		i = (endptr - *buffer);
diff --git a/ext/standard/mt_rand.c b/ext/standard/mt_rand.c
index 366221d953443..25f6a431a57a0 100644
--- a/ext/standard/mt_rand.c
+++ b/ext/standard/mt_rand.c
@@ -100,9 +100,9 @@ static inline void php_mt_initialize(uint32_t seed, uint32_t *state)
 	   In previous versions, most significant bits (MSBs) of the seed affect
 	   only MSBs of the state array.  Modified 9 Jan 2002 by Makoto Matsumoto. */
 
-	register uint32_t *s = state;
-	register uint32_t *r = state;
-	register int i = 1;
+	uint32_t *s = state;
+	uint32_t *r = state;
+	int i = 1;
 
 	*s++ = seed & 0xffffffffU;
 	for( ; i < N; ++i ) {
@@ -118,9 +118,9 @@ static inline void php_mt_reload(void)
 	/* Generate N new values in state
 	   Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
 
-	register uint32_t *state = BG(state);
-	register uint32_t *p = state;
-	register int i;
+	uint32_t *state = BG(state);
+	uint32_t *p = state;
+	int i;
 
 	if (BG(mt_rand_mode) == MT_RAND_MT19937) {
 		for (i = N - M; i--; ++p)
@@ -159,7 +159,7 @@ PHPAPI uint32_t php_mt_rand(void)
 	/* Pull a 32-bit integer from the generator state
 	   Every other access function simply transforms the numbers extracted here */
 
-	register uint32_t s1;
+	uint32_t s1;
 
 	if (UNEXPECTED(!BG(mt_rand_is_seeded))) {
 		zend_long bytes;
diff --git a/ext/standard/pack.c b/ext/standard/pack.c
index 3fad071aab662..bfad808121680 100644
--- a/ext/standard/pack.c
+++ b/ext/standard/pack.c
@@ -739,24 +739,24 @@ PHP_FUNCTION(unpack)
 	while (formatlen-- > 0) {
 		char type = *(format++);
 		char c;
-		int arg = 1, argb;
+		int repetitions = 1, argb;
 		char *name;
 		int namelen;
-		int size=0;
+		int size = 0;
 
 		/* Handle format arguments if any */
 		if (formatlen > 0) {
 			c = *format;
 
 			if (c >= '0' && c <= '9') {
-				arg = atoi(format);
+				repetitions = atoi(format);
 
 				while (formatlen > 0 && *format >= '0' && *format <= '9') {
 					format++;
 					formatlen--;
 				}
 			} else if (c == '*') {
-				arg = -1;
+				repetitions = -1;
 				format++;
 				formatlen--;
 			}
@@ -764,7 +764,7 @@ PHP_FUNCTION(unpack)
 
 		/* Get of new value in array */
 		name = format;
-		argb = arg;
+		argb = repetitions;
 
 		while (formatlen > 0 && *format != '/') {
 			formatlen--;
@@ -780,9 +780,9 @@ PHP_FUNCTION(unpack)
 			/* Never use any input */
 			case 'X':
 				size = -1;
-				if (arg < 0) {
+				if (repetitions < 0) {
 					php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", type);
-					arg = 1;
+					repetitions = 1;
 				}
 				break;
 
@@ -793,14 +793,14 @@ PHP_FUNCTION(unpack)
 			case 'a':
 			case 'A':
 			case 'Z':
-				size = arg;
-				arg = 1;
+				size = repetitions;
+				repetitions = 1;
 				break;
 
 			case 'h':
 			case 'H':
-				size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
-				arg = 1;
+				size = (repetitions > 0) ? (repetitions + (repetitions % 2)) / 2 : repetitions;
+				repetitions = 1;
 				break;
 
 			/* Use 1 byte of input */
@@ -870,18 +870,9 @@ PHP_FUNCTION(unpack)
 			RETURN_FALSE;
 		}
 
-		/* Do actual unpacking */
-		for (i = 0; i != arg; i++ ) {
-			/* Space for name + number, safe as namelen is ensured <= 200 */
-			char n[256];
 
-			if (arg != 1 || namelen == 0) {
-				/* Need to add element number to name */
-				snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
-			} else {
-				/* Truncate name to next format code or end of string */
-				snprintf(n, sizeof(n), "%.*s", namelen, name);
-			}
+		/* Do actual unpacking */
+		for (i = 0; i != repetitions; i++ ) {
 
 			if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
 				php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
@@ -890,6 +881,22 @@ PHP_FUNCTION(unpack)
 			}
 
 			if ((inputpos + size) <= inputlen) {
+
+				zend_string* real_name;
+				zval val;
+
+				if (repetitions == 1 && namelen > 0) {
+					/* Use a part of the formatarg argument directly as the name. */
+					real_name = zend_string_init_fast(name, namelen);
+
+				} else {
+					/* Need to add the 1-based element number to the name */
+					char buf[MAX_LENGTH_OF_LONG + 1];
+					char *res = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, i+1);
+					size_t digits = buf + sizeof(buf) - 1 - res;
+					real_name = zend_string_concat2(name, namelen, res, digits);
+				}
+
 				switch ((int) type) {
 					case 'a': {
 						/* a will not strip any trailing whitespace or null padding */
@@ -902,7 +909,8 @@ PHP_FUNCTION(unpack)
 
 						size = len;
 
-						add_assoc_stringl(return_value, n, &input[inputpos], len);
+						ZVAL_STRINGL(&val, &input[inputpos], len);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 					case 'A': {
@@ -928,7 +936,8 @@ PHP_FUNCTION(unpack)
 								break;
 						}
 
-						add_assoc_stringl(return_value, n, &input[inputpos], len + 1);
+						ZVAL_STRINGL(&val, &input[inputpos], len + 1);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 					/* New option added for Z to remain in-line with the Perl implementation */
@@ -952,7 +961,8 @@ PHP_FUNCTION(unpack)
 						}
 						len = s;
 
-						add_assoc_stringl(return_value, n, &input[inputpos], len);
+						ZVAL_STRINGL(&val, &input[inputpos], len);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 
@@ -995,7 +1005,9 @@ PHP_FUNCTION(unpack)
 						}
 
 						ZSTR_VAL(buf)[len] = '\0';
-						add_assoc_str(return_value, n, buf);
+
+						ZVAL_STR(&val, buf);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 
@@ -1003,7 +1015,9 @@ PHP_FUNCTION(unpack)
 					case 'C': { /* unsigned */
 						uint8_t x = input[inputpos];
 						zend_long v = (type == 'c') ? (int8_t) x : x;
-						add_assoc_long(return_value, n, v);
+
+						ZVAL_LONG(&val, v);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 
@@ -1022,7 +1036,8 @@ PHP_FUNCTION(unpack)
 							v = x;
 						}
 
-						add_assoc_long(return_value, n, v);
+						ZVAL_LONG(&val, v);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 
@@ -1030,7 +1045,9 @@ PHP_FUNCTION(unpack)
 					case 'I': { /* unsigned integer, machine size, machine endian */
 						unsigned int x = *((unaligned_uint*) &input[inputpos]);
 						zend_long v = (type == 'i') ? (int) x : x;
-						add_assoc_long(return_value, n, v);
+
+						ZVAL_LONG(&val, v);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 
@@ -1049,7 +1066,9 @@ PHP_FUNCTION(unpack)
 							v = x;
 						}
 
-						add_assoc_long(return_value, n, v);
+						ZVAL_LONG(&val, v);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
+
 						break;
 					}
 
@@ -1069,7 +1088,8 @@ PHP_FUNCTION(unpack)
 							v = x;
 						}
 
-						add_assoc_long(return_value, n, v);
+						ZVAL_LONG(&val, v);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 #endif
@@ -1088,7 +1108,8 @@ PHP_FUNCTION(unpack)
 							memcpy(&v, &input[inputpos], sizeof(float));
 						}
 
-						add_assoc_double(return_value, n, (double)v);
+						ZVAL_DOUBLE(&val, v);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 
@@ -1105,7 +1126,9 @@ PHP_FUNCTION(unpack)
 						} else {
 							memcpy(&v, &input[inputpos], sizeof(double));
 						}
-						add_assoc_double(return_value, n, v);
+
+						ZVAL_DOUBLE(&val, v);
+						zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
 						break;
 					}
 
@@ -1116,25 +1139,27 @@ PHP_FUNCTION(unpack)
 					case 'X':
 						if (inputpos < size) {
 							inputpos = -size;
-							i = arg - 1;		/* Break out of for loop */
+							i = repetitions - 1;		/* Break out of for loop */
 
-							if (arg >= 0) {
+							if (repetitions >= 0) {
 								php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
 							}
 						}
 						break;
 
 					case '@':
-						if (arg <= inputlen) {
-							inputpos = arg;
+						if (repetitions <= inputlen) {
+							inputpos = repetitions;
 						} else {
 							php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
 						}
 
-						i = arg - 1;	/* Done, break out of for loop */
+						i = repetitions - 1;	/* Done, break out of for loop */
 						break;
 				}
 
+				zend_string_release(real_name);
+
 				inputpos += size;
 				if (inputpos < 0) {
 					if (size != -1) { /* only print warning if not working with * */
@@ -1142,7 +1167,7 @@ PHP_FUNCTION(unpack)
 					}
 					inputpos = 0;
 				}
-			} else if (arg < 0) {
+			} else if (repetitions < 0) {
 				/* Reached end of input for '*' repeater */
 				break;
 			} else {
diff --git a/ext/standard/quot_print.c b/ext/standard/quot_print.c
index 3cdf7c35016c0..f5472fc386d14 100644
--- a/ext/standard/quot_print.c
+++ b/ext/standard/quot_print.c
@@ -49,10 +49,10 @@ static char php_hex2int(int c) /* {{{ */
 
 PHPAPI zend_string *php_quot_print_decode(const unsigned char *str, size_t length, int replace_us_by_ws) /* {{{ */
 {
-	register size_t i;
-	register unsigned const char *p1;
-	register unsigned char *p2;
-	register unsigned int h_nbl, l_nbl;
+	size_t i;
+	unsigned const char *p1;
+	unsigned char *p2;
+	unsigned int h_nbl, l_nbl;
 
 	size_t decoded_len, buf_size;
 	zend_string *retval;
diff --git a/ext/standard/string.c b/ext/standard/string.c
index efb13686a9edb..e97c02eef60f7 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -1367,7 +1367,7 @@ PHPAPI zend_string *php_string_toupper(zend_string *s)
 
 	while (c < e) {
 		if (islower(*c)) {
-			register unsigned char *r;
+			unsigned char *r;
 			zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
 
 			if (c != (unsigned char*)ZSTR_VAL(s)) {
@@ -1432,7 +1432,7 @@ PHPAPI zend_string *php_string_tolower(zend_string *s)
 
 		while (c < e) {
 			if (isupper(*c)) {
-				register unsigned char *r;
+				unsigned char *r;
 				zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
 
 				if (c != (unsigned char*)ZSTR_VAL(s)) {
@@ -1764,8 +1764,8 @@ PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
 /* {{{ php_strspn */
 PHPAPI size_t php_strspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end)
 {
-	register const char *p = s1, *spanp;
-	register char c = *p;
+	const char *p = s1, *spanp;
+	char c = *p;
 
 cont:
 	for (spanp = s2; p != s1_end && spanp != s2_end;) {
@@ -1781,8 +1781,8 @@ PHPAPI size_t php_strspn(const char *s1, const char *s2, const char *s1_end, con
 /* {{{ php_strcspn */
 PHPAPI size_t php_strcspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end)
 {
-	register const char *p, *spanp;
-	register char c = *s1;
+	const char *p, *spanp;
+	char c = *s1;
 
 	for (p = s1;;) {
 		spanp = s2;
@@ -2667,8 +2667,8 @@ PHP_FUNCTION(ucwords)
 {
 	zend_string *str;
 	char *delims = " \t\r\n\f\v";
-	register char *r;
-	register const char *r_end;
+	char *r;
+	const char *r_end;
 	size_t delims_len = 6;
 	char mask[256];
 
diff --git a/ext/standard/tests/strings/pack_arrays.phpt b/ext/standard/tests/strings/pack_arrays.phpt
new file mode 100644
index 0000000000000..301897006c1b4
--- /dev/null
+++ b/ext/standard/tests/strings/pack_arrays.phpt
@@ -0,0 +1,42 @@
+--TEST--
+test unpack() to array with named keys
+--FILE--
+<?php
+$str = pack('VVV', 0x00010203, 0x04050607, 0x08090a0b);
+print_r(unpack('Vaa/Vbb/Vcc', $str));
+print_r(unpack('V2aa/Vcc', $str));
+print_r(unpack('V3aa', $str));
+print_r(unpack('V*aa', $str));
+print_r(unpack('V*', $str));
+?>
+--EXPECT--
+Array
+(
+    [aa] => 66051
+    [bb] => 67438087
+    [cc] => 134810123
+)
+Array
+(
+    [aa1] => 66051
+    [aa2] => 67438087
+    [cc] => 134810123
+)
+Array
+(
+    [aa1] => 66051
+    [aa2] => 67438087
+    [aa3] => 134810123
+)
+Array
+(
+    [aa1] => 66051
+    [aa2] => 67438087
+    [aa3] => 134810123
+)
+Array
+(
+    [1] => 66051
+    [2] => 67438087
+    [3] => 134810123
+)
diff --git a/ext/standard/url.c b/ext/standard/url.c
index 0614095b8f2d7..efad0d77c2c36 100644
--- a/ext/standard/url.c
+++ b/ext/standard/url.c
@@ -459,7 +459,7 @@ static int php_htoi(char *s)
 static const unsigned char hexchars[] = "0123456789ABCDEF";
 
 static zend_always_inline zend_string *php_url_encode_impl(const char *s, size_t len, bool raw) /* {{{ */ {
-	register unsigned char c;
+	unsigned char c;
 	unsigned char *to;
 	unsigned char const *from, *end;
 	zend_string *start;
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 4c82e5db80ce6..2ed24b79b9459 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -612,7 +612,7 @@ PHP_FUNCTION(gzfile)
 	size_t filename_len;
 	int flags = REPORT_ERRORS;
 	char buf[8192] = {0};
-	register int i = 0;
+	int i = 0;
 	zend_long use_include_path = 0;
 	php_stream *stream;
 
diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c
index 7b11ebd5492cc..6b05da6117a51 100644
--- a/main/fopen_wrappers.c
+++ b/main/fopen_wrappers.c
@@ -688,7 +688,7 @@ PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const c
 /* {{{ php_strip_url_passwd */
 PHPAPI char *php_strip_url_passwd(char *url)
 {
-	register char *p, *url_start;
+	char *p, *url_start;
 
 	if (url == NULL) {
 		return "";
diff --git a/main/rfc1867.c b/main/rfc1867.c
index 583b3166d537f..315e87b041425 100644
--- a/main/rfc1867.c
+++ b/main/rfc1867.c
@@ -55,7 +55,7 @@ PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **
 static void safe_php_register_variable(char *var, char *strval, size_t val_len, zval *track_vars_array, bool override_protection);
 
 /* The longest property name we use in an uploaded file array */
-#define MAX_SIZE_OF_INDEX sizeof("[tmp_name]")
+#define MAX_SIZE_OF_INDEX sizeof("[full_path]")
 
 /* The longest anonymous name */
 #define MAX_SIZE_ANONNAME 33
@@ -1142,9 +1142,20 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
 				snprintf(lbuf, llen, "%s[name]", param);
 			}
 			register_http_post_files_variable(lbuf, s, &PG(http_globals)[TRACK_VARS_FILES], 0);
-			efree(filename);
 			s = NULL;
 
+			/* Add full path of supplied file for folder uploads via 
+			 * <input type="file" name="files" multiple webkitdirectory>
+			 */
+			/* Add $foo[full_path] */
+			if (is_arr_upload) {
+				snprintf(lbuf, llen, "%s[full_path][%s]", abuf, array_index);
+			} else {
+				snprintf(lbuf, llen, "%s[full_path]", param);
+			}
+			register_http_post_files_variable(lbuf, filename, &PG(http_globals)[TRACK_VARS_FILES], 0);
+			efree(filename);
+
 			/* Possible Content-Type: */
 			if (cancel_upload || !(cd = php_mime_get_hdr_value(header, "Content-Type"))) {
 				cd = "";
diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c
index e0a1191c3de3e..a6c47e185909b 100644
--- a/sapi/cli/php_cli.c
+++ b/sapi/cli/php_cli.c
@@ -488,11 +488,7 @@ static void php_cli_usage(char *argv0)
 				"   %s [options] -- [args...]\n"
 				"   %s [options] -a\n"
 				"\n"
-#if (defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)) && !defined(COMPILE_DL_READLINE)
-				"  -a               Run as interactive shell\n"
-#else
-				"  -a               Run interactively\n"
-#endif
+				"  -a               Run as interactive shell (requires readline extension)\n"
 				"  -c <path>|<file> Look for php.ini file in this directory\n"
 				"  -n               No configuration (ini) files will be used\n"
 				"  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
@@ -694,6 +690,10 @@ static int do_cli(int argc, char **argv) /* {{{ */
 			switch (c) {
 
 			case 'a':	/* interactive mode */
+				if (!cli_shell_callbacks.cli_shell_run) {
+					param_error = "Interactive shell (-a) requires the readline extension.\n";
+					break;
+				}
 				if (!interactive) {
 					if (behavior != PHP_MODE_STANDARD) {
 						param_error = param_mode_conflict;
@@ -874,13 +874,7 @@ static int do_cli(int argc, char **argv) /* {{{ */
 #endif
 
 		if (interactive) {
-			if (cli_shell_callbacks.cli_shell_run) {
-				printf("Interactive shell\n\n");
-			} else {
-				printf("Interactive mode enabled\n\n");
-				/* Treat as non-interactive apart from the stdin input */
-				interactive = false;
-			}
+			printf("Interactive shell\n\n");
 			fflush(stdout);
 		}
 
diff --git a/sapi/cli/tests/009.phpt b/sapi/cli/tests/009.phpt
index c110966458326..904f680c7f378 100644
--- a/sapi/cli/tests/009.phpt
+++ b/sapi/cli/tests/009.phpt
@@ -1,5 +1,7 @@
 --TEST--
 using invalid combinations of cmdline options
+--EXTENSIONS--
+readline
 --SKIPIF--
 <?php include "skipif.inc"; ?>
 --FILE--
diff --git a/sapi/cli/tests/012-2.phpt b/sapi/cli/tests/012-2.phpt
index da73fa9b26f22..8ef97595691e3 100644
--- a/sapi/cli/tests/012-2.phpt
+++ b/sapi/cli/tests/012-2.phpt
@@ -1,5 +1,7 @@
 --TEST--
 more invalid arguments and error messages
+--EXTENSIONS--
+readline
 --SKIPIF--
 <?php include "skipif.inc"; ?>
 --FILE--
diff --git a/sapi/cli/tests/php_cli_server_005.phpt b/sapi/cli/tests/php_cli_server_005.phpt
index 41e57881a10b9..339f25424499b 100644
--- a/sapi/cli/tests/php_cli_server_005.phpt
+++ b/sapi/cli/tests/php_cli_server_005.phpt
@@ -52,9 +52,11 @@ Content-type: text/html; charset=UTF-8
 
 array(1) {
   ["userfile"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(12) "laruence.txt"
+    ["full_path"]=>
+    string(12) "laruence.txt"
     ["type"]=>
     string(10) "text/plain"
     ["tmp_name"]=>
diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c
index bc582fe9cecce..0464a36f16801 100644
--- a/sapi/fpm/fpm/fpm_conf.c
+++ b/sapi/fpm/fpm/fpm_conf.c
@@ -208,9 +208,9 @@ static int fpm_conf_expand_pool_name(char **value) {
 
 static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset) /* {{{ */
 {
-	char *val = Z_STRVAL_P(value);
-	long value_y = !strcasecmp(val, "1");
-	long value_n = !strcasecmp(val, "");
+	zend_string *val = Z_STR_P(value);
+	bool value_y = zend_string_equals_literal(val, "1");
+	bool value_n = ZSTR_LEN(val) == 0; /* Empty string is the only valid false value */
 
 	if (!value_y && !value_n) {
 		return "invalid boolean value";
@@ -324,18 +324,18 @@ static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset) /* {
 
 static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset) /* {{{ */
 {
-	char *val = Z_STRVAL_P(value);
+	zend_string *val = Z_STR_P(value);
 	int log_level;
 
-	if (!strcasecmp(val, "debug")) {
+	if (zend_string_equals_literal_ci(val, "debug")) {
 		log_level = ZLOG_DEBUG;
-	} else if (!strcasecmp(val, "notice")) {
+	} else if (zend_string_equals_literal_ci(val, "notice")) {
 		log_level = ZLOG_NOTICE;
-	} else if (!strcasecmp(val, "warning") || !strcasecmp(val, "warn")) {
+	} else if (zend_string_equals_literal_ci(val, "warning") || zend_string_equals_literal_ci(val, "warn")) {
 		log_level = ZLOG_WARNING;
-	} else if (!strcasecmp(val, "error")) {
+	} else if (zend_string_equals_literal_ci(val, "error")) {
 		log_level = ZLOG_ERROR;
-	} else if (!strcasecmp(val, "alert")) {
+	} else if (zend_string_equals_literal_ci(val, "alert")) {
 		log_level = ZLOG_ALERT;
 	} else {
 		return "invalid value for 'log_level'";
@@ -349,144 +349,144 @@ static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset)
 #ifdef HAVE_SYSLOG_H
 static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset) /* {{{ */
 {
-	char *val = Z_STRVAL_P(value);
+	zend_string *val = Z_STR_P(value);
 	int *conf = (int *) ((char *) *config + offset);
 
 #ifdef LOG_AUTH
-	if (!strcasecmp(val, "AUTH")) {
+	if (zend_string_equals_literal_ci(val, "AUTH")) {
 		*conf = LOG_AUTH;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_AUTHPRIV
-	if (!strcasecmp(val, "AUTHPRIV")) {
+	if (zend_string_equals_literal_ci(val, "AUTHPRIV")) {
 		*conf = LOG_AUTHPRIV;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_CRON
-	if (!strcasecmp(val, "CRON")) {
+	if (zend_string_equals_literal_ci(val, "CRON")) {
 		*conf = LOG_CRON;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_DAEMON
-	if (!strcasecmp(val, "DAEMON")) {
+	if (zend_string_equals_literal_ci(val, "DAEMON")) {
 		*conf = LOG_DAEMON;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_FTP
-	if (!strcasecmp(val, "FTP")) {
+	if (zend_string_equals_literal_ci(val, "FTP")) {
 		*conf = LOG_FTP;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_KERN
-	if (!strcasecmp(val, "KERN")) {
+	if (zend_string_equals_literal_ci(val, "KERN")) {
 		*conf = LOG_KERN;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LPR
-	if (!strcasecmp(val, "LPR")) {
+	if (zend_string_equals_literal_ci(val, "LPR")) {
 		*conf = LOG_LPR;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_MAIL
-	if (!strcasecmp(val, "MAIL")) {
+	if (zend_string_equals_literal_ci(val, "MAIL")) {
 		*conf = LOG_MAIL;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_NEWS
-	if (!strcasecmp(val, "NEWS")) {
+	if (zend_string_equals_literal_ci(val, "NEWS")) {
 		*conf = LOG_NEWS;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_SYSLOG
-	if (!strcasecmp(val, "SYSLOG")) {
+	if (zend_string_equals_literal_ci(val, "SYSLOG")) {
 		*conf = LOG_SYSLOG;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_USER
-	if (!strcasecmp(val, "USER")) {
+	if (zend_string_equals_literal_ci(val, "USER")) {
 		*conf = LOG_USER;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_UUCP
-	if (!strcasecmp(val, "UUCP")) {
+	if (zend_string_equals_literal_ci(val, "UUCP")) {
 		*conf = LOG_UUCP;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL0
-	if (!strcasecmp(val, "LOCAL0")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL0")) {
 		*conf = LOG_LOCAL0;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL1
-	if (!strcasecmp(val, "LOCAL1")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL1")) {
 		*conf = LOG_LOCAL1;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL2
-	if (!strcasecmp(val, "LOCAL2")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL2")) {
 		*conf = LOG_LOCAL2;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL3
-	if (!strcasecmp(val, "LOCAL3")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL3")) {
 		*conf = LOG_LOCAL3;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL4
-	if (!strcasecmp(val, "LOCAL4")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL4")) {
 		*conf = LOG_LOCAL4;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL5
-	if (!strcasecmp(val, "LOCAL5")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL5")) {
 		*conf = LOG_LOCAL5;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL6
-	if (!strcasecmp(val, "LOCAL6")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL6")) {
 		*conf = LOG_LOCAL6;
 		return NULL;
 	}
 #endif
 
 #ifdef LOG_LOCAL7
-	if (!strcasecmp(val, "LOCAL7")) {
+	if (zend_string_equals_literal_ci(val, "LOCAL7")) {
 		*conf = LOG_LOCAL7;
 		return NULL;
 	}
@@ -499,10 +499,10 @@ static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t o
 
 static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset) /* {{{ */
 {
-	char *val = Z_STRVAL_P(value);
+	zend_string *val = Z_STR_P(value);
 	int *ptr = (int *) ((char *) *config + offset);
 
-	if (!strcasecmp(val, "unlimited")) {
+	if (zend_string_equals_literal_ci(val, "unlimited")) {
 		*ptr = -1;
 	} else {
 		int int_value;
@@ -528,13 +528,13 @@ static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offse
 
 static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset) /* {{{ */
 {
-	char *val = Z_STRVAL_P(value);
+	zend_string *val = Z_STR_P(value);
 	struct fpm_worker_pool_config_s  *c = *config;
-	if (!strcasecmp(val, "static")) {
+	if (zend_string_equals_literal_ci(val, "static")) {
 		c->pm = PM_STYLE_STATIC;
-	} else if (!strcasecmp(val, "dynamic")) {
+	} else if (zend_string_equals_literal_ci(val, "dynamic")) {
 		c->pm = PM_STYLE_DYNAMIC;
-	} else if (!strcasecmp(val, "ondemand")) {
+	} else if (zend_string_equals_literal_ci(val, "ondemand")) {
 		c->pm = PM_STYLE_ONDEMAND;
 	} else {
 		return "invalid process manager (static, dynamic or ondemand)";
@@ -1403,7 +1403,7 @@ static void fpm_conf_ini_parser_section(zval *section, void *arg) /* {{{ */
 	int *error = (int *)arg;
 
 	/* switch to global conf */
-	if (!strcasecmp(Z_STRVAL_P(section), "global")) {
+	if (zend_string_equals_literal_ci(Z_STR_P(section), "global")) {
 		current_wp = NULL;
 		return;
 	}
@@ -1446,7 +1446,7 @@ static void fpm_conf_ini_parser_entry(zval *name, zval *value, void *arg) /* {{{
 		return;
 	}
 
-	if (!strcmp(Z_STRVAL_P(name), "include")) {
+	if (zend_string_equals_literal(Z_STR_P(name), "include")) {
 		if (ini_include) {
 			zlog(ZLOG_ERROR, "[%s:%d] two includes at the same time !", ini_filename, ini_lineno);
 			*error = 1;
@@ -1508,7 +1508,7 @@ static void fpm_conf_ini_parser_array(zval *name, zval *key, zval *value, void *
 		return;
 	}
 
-	if (!strcmp("env", Z_STRVAL_P(name))) {
+	if (zend_string_equals_literal(Z_STR_P(name), "env")) {
 		if (!*Z_STRVAL_P(value)) {
 			zlog(ZLOG_ERROR, "[%s:%d] empty value", ini_filename, ini_lineno);
 			*error = 1;
@@ -1517,19 +1517,19 @@ static void fpm_conf_ini_parser_array(zval *name, zval *key, zval *value, void *
 		config = (char *)current_wp->config + WPO(env);
 		err = fpm_conf_set_array(key, value, &config, 0);
 
-	} else if (!strcmp("php_value", Z_STRVAL_P(name))) {
+	} else if (zend_string_equals_literal(Z_STR_P(name), "php_value")) {
 		config = (char *)current_wp->config + WPO(php_values);
 		err = fpm_conf_set_array(key, value, &config, 0);
 
-	} else if (!strcmp("php_admin_value", Z_STRVAL_P(name))) {
+	} else if (zend_string_equals_literal(Z_STR_P(name), "php_admin_value")) {
 		config = (char *)current_wp->config + WPO(php_admin_values);
 		err = fpm_conf_set_array(key, value, &config, 0);
 
-	} else if (!strcmp("php_flag", Z_STRVAL_P(name))) {
+	} else if (zend_string_equals_literal(Z_STR_P(name), "php_flag")) {
 		config = (char *)current_wp->config + WPO(php_values);
 		err = fpm_conf_set_array(key, value, &config, 1);
 
-	} else if (!strcmp("php_admin_flag", Z_STRVAL_P(name))) {
+	} else if (zend_string_equals_literal(Z_STR_P(name), "php_admin_flag")) {
 		config = (char *)current_wp->config + WPO(php_admin_values);
 		err = fpm_conf_set_array(key, value, &config, 1);
 
diff --git a/sapi/phpdbg/Makefile.frag b/sapi/phpdbg/Makefile.frag
index 1572bfd9f078e..2e2faf803b00d 100644
--- a/sapi/phpdbg/Makefile.frag
+++ b/sapi/phpdbg/Makefile.frag
@@ -29,13 +29,3 @@ install-phpdbg: $(BUILD_BINARY)
 	@echo "Installing phpdbg man page:       $(INSTALL_ROOT)$(mandir)/man1/"
 	@$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man1
 	@$(INSTALL_DATA) sapi/phpdbg/phpdbg.1 $(INSTALL_ROOT)$(mandir)/man1/$(program_prefix)phpdbg$(program_suffix).1
-
-clean-phpdbg:
-	@echo "Cleaning phpdbg object files ..."
-	find sapi/phpdbg/ -name *.lo -o -name *.o | xargs rm -f
-
-test-phpdbg:
-	@echo "Running phpdbg tests ..."
-	@$(top_builddir)/sapi/cli/php sapi/phpdbg/tests/run-tests.php --phpdbg sapi/phpdbg/phpdbg
-
-.PHONY: clean-phpdbg test-phpdbg
diff --git a/tests/basic/021.phpt b/tests/basic/021.phpt
index eeaf58869b0b5..37e853e58ac70 100644
--- a/tests/basic/021.phpt
+++ b/tests/basic/021.phpt
@@ -24,9 +24,11 @@ var_dump($_POST);
 --EXPECTF--
 array(1) {
   ["pics"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(12) "bug37276.txt"
+    ["full_path"]=>
+    string(12) "bug37276.txt"
     ["type"]=>
     string(10) "text/plain"
     ["tmp_name"]=>
diff --git a/tests/basic/029.phpt b/tests/basic/029.phpt
index 21d9082cffb8b..d720cbc6ba9a0 100644
--- a/tests/basic/029.phpt
+++ b/tests/basic/029.phpt
@@ -32,9 +32,11 @@ var_dump($_POST);
 --EXPECTF--
 array(1) {
   ["pics"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(10) "text/plain"
     ["tmp_name"]=>
diff --git a/tests/basic/bug55500.phpt b/tests/basic/bug55500.phpt
index 2f9e393348aec..4f2387e0f184e 100644
--- a/tests/basic/bug55500.phpt
+++ b/tests/basic/bug55500.phpt
@@ -36,12 +36,17 @@ var_dump($_POST);
 --EXPECTF--
 array(1) {
   ["file"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     array(1) {
       [0]=>
       string(9) "file1.txt"
     }
+    ["full_path"]=>
+    array(1) {
+      [0]=>
+      string(9) "file1.txt"
+    }
     ["type"]=>
     array(1) {
       [0]=>
diff --git a/tests/basic/bug73969.phpt b/tests/basic/bug73969.phpt
index 11cfecd16b241..571bc33b1bb5b 100644
--- a/tests/basic/bug73969.phpt
+++ b/tests/basic/bug73969.phpt
@@ -26,5 +26,5 @@ class c1
 c1::go();
 ?>
 --EXPECTF--
-#0  require() called at [%s:19]
-#1  c1::go() called at [%s:23]
+#0 %s(19): require()
+#1 %s(23): c1::go()
diff --git a/tests/basic/rfc1867_anonymous_upload.phpt b/tests/basic/rfc1867_anonymous_upload.phpt
index 5650b5cd5db09..923ee3e258d1c 100644
--- a/tests/basic/rfc1867_anonymous_upload.phpt
+++ b/tests/basic/rfc1867_anonymous_upload.phpt
@@ -25,9 +25,11 @@ var_dump($_POST);
 --EXPECTF--
 array(2) {
   [%d]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(16) "text/plain-file1"
     ["tmp_name"]=>
@@ -38,9 +40,11 @@ array(2) {
     int(1)
   }
   [%d]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(16) "text/plain-file2"
     ["tmp_name"]=>
diff --git a/tests/basic/rfc1867_array_upload.phpt b/tests/basic/rfc1867_array_upload.phpt
index 90ed0c36e02b5..9f48e59913c50 100644
--- a/tests/basic/rfc1867_array_upload.phpt
+++ b/tests/basic/rfc1867_array_upload.phpt
@@ -30,7 +30,7 @@ var_dump($_POST);
 --EXPECTF--
 array(1) {
   ["file"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     array(3) {
       [0]=>
@@ -40,6 +40,15 @@ array(1) {
       [3]=>
       string(9) "file3.txt"
     }
+    ["full_path"]=>
+    array(3) {
+      [0]=>
+      string(9) "file1.txt"
+      [2]=>
+      string(9) "file2.txt"
+      [3]=>
+      string(9) "file3.txt"
+    }
     ["type"]=>
     array(3) {
       [0]=>
diff --git a/tests/basic/rfc1867_empty_upload.phpt b/tests/basic/rfc1867_empty_upload.phpt
index 2b89ca8888b71..c8a96955be905 100644
--- a/tests/basic/rfc1867_empty_upload.phpt
+++ b/tests/basic/rfc1867_empty_upload.phpt
@@ -40,9 +40,11 @@ if (is_uploaded_file($_FILES["file3"]["tmp_name"])) {
 --EXPECTF--
 array(3) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(16) "text/plain-file1"
     ["tmp_name"]=>
@@ -53,9 +55,11 @@ array(3) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(0) ""
+    ["full_path"]=>
+    string(0) ""
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -66,9 +70,11 @@ array(3) {
     int(0)
   }
   ["file3"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file3.txt"
+    ["full_path"]=>
+    string(9) "file3.txt"
     ["type"]=>
     string(16) "text/plain-file3"
     ["tmp_name"]=>
diff --git a/tests/basic/rfc1867_max_file_size.phpt b/tests/basic/rfc1867_max_file_size.phpt
index 8d585f750322b..81133e30a2dec 100644
--- a/tests/basic/rfc1867_max_file_size.phpt
+++ b/tests/basic/rfc1867_max_file_size.phpt
@@ -40,9 +40,11 @@ if (is_uploaded_file($_FILES["file3"]["tmp_name"])) {
 --EXPECTF--
 array(3) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(16) "text/plain-file1"
     ["tmp_name"]=>
@@ -53,9 +55,11 @@ array(3) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -66,9 +70,11 @@ array(3) {
     int(0)
   }
   ["file3"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file3.txt"
+    ["full_path"]=>
+    string(20) "C:\foo\bar/file3.txt"
     ["type"]=>
     string(16) "text/plain-file3"
     ["tmp_name"]=>
diff --git a/tests/basic/rfc1867_max_file_uploads_empty_files.phpt b/tests/basic/rfc1867_max_file_uploads_empty_files.phpt
index b85ed20971989..e95b8454d1337 100644
--- a/tests/basic/rfc1867_max_file_uploads_empty_files.phpt
+++ b/tests/basic/rfc1867_max_file_uploads_empty_files.phpt
@@ -40,9 +40,11 @@ if (is_uploaded_file($_FILES["file4"]["tmp_name"])) {
 --EXPECTF--
 array(4) {
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(0) ""
+    ["full_path"]=>
+    string(0) ""
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -53,9 +55,11 @@ array(4) {
     int(0)
   }
   ["file3"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(0) ""
+    ["full_path"]=>
+    string(0) ""
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -66,9 +70,11 @@ array(4) {
     int(0)
   }
   ["file4"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file4.txt"
+    ["full_path"]=>
+    string(9) "file4.txt"
     ["type"]=>
     string(15) "text/plain-file"
     ["tmp_name"]=>
@@ -79,9 +85,11 @@ array(4) {
     int(0)
   }
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(15) "text/plain-file"
     ["tmp_name"]=>
diff --git a/tests/basic/rfc1867_missing_boundary_2.phpt b/tests/basic/rfc1867_missing_boundary_2.phpt
index d3f93f838715c..7011a2d86b8dd 100644
--- a/tests/basic/rfc1867_missing_boundary_2.phpt
+++ b/tests/basic/rfc1867_missing_boundary_2.phpt
@@ -18,9 +18,11 @@ var_dump($_POST);
 --EXPECT--
 array(1) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
diff --git a/tests/basic/rfc1867_multiple_webkitdirectory.phpt b/tests/basic/rfc1867_multiple_webkitdirectory.phpt
new file mode 100644
index 0000000000000..00dc12fda6086
--- /dev/null
+++ b/tests/basic/rfc1867_multiple_webkitdirectory.phpt
@@ -0,0 +1,74 @@
+--TEST--
+Request #77372 (Relative file path is removed from uploaded file)
+--INI--
+file_uploads=1
+upload_max_filesize=1024
+max_file_uploads=10
+--POST_RAW--
+Content-Type: multipart/form-data; boundary=---------------------------64369134225794159231042985467
+-----------------------------64369134225794159231042985467
+Content-Disposition: form-data; name="files[]"; filename="directory/subdirectory/file2.txt"
+Content-Type: text/plain
+
+2
+-----------------------------64369134225794159231042985467
+Content-Disposition: form-data; name="files[]"; filename="directory/file1.txt"
+Content-Type: text/plain
+
+1
+-----------------------------64369134225794159231042985467--
+--FILE--
+<?php
+var_dump($_FILES);
+var_dump($_POST);
+?>
+--EXPECTF--
+array(1) {
+  ["files"]=>
+  array(6) {
+    ["name"]=>
+    array(2) {
+      [0]=>
+      string(9) "file2.txt"
+      [1]=>
+      string(9) "file1.txt"
+    }
+    ["full_path"]=>
+    array(2) {
+      [0]=>
+      string(32) "directory/subdirectory/file2.txt"
+      [1]=>
+      string(19) "directory/file1.txt"
+    }
+    ["type"]=>
+    array(2) {
+      [0]=>
+      string(10) "text/plain"
+      [1]=>
+      string(10) "text/plain"
+    }
+    ["tmp_name"]=>
+    array(2) {
+      [0]=>
+      string(%d) "%s"
+      [1]=>
+      string(%d) "%s"
+    }
+    ["error"]=>
+    array(2) {
+      [0]=>
+      int(0)
+      [1]=>
+      int(0)
+    }
+    ["size"]=>
+    array(2) {
+      [0]=>
+      int(1)
+      [1]=>
+      int(1)
+    }
+  }
+}
+array(0) {
+}
diff --git a/tests/basic/rfc1867_post_max_filesize.phpt b/tests/basic/rfc1867_post_max_filesize.phpt
index b03220e915a7b..f8e99e574fd08 100644
--- a/tests/basic/rfc1867_post_max_filesize.phpt
+++ b/tests/basic/rfc1867_post_max_filesize.phpt
@@ -36,9 +36,11 @@ if (is_uploaded_file($_FILES["file3"]["tmp_name"])) {
 --EXPECTF--
 array(3) {
   ["file1"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file1.txt"
+    ["full_path"]=>
+    string(9) "file1.txt"
     ["type"]=>
     string(16) "text/plain-file1"
     ["tmp_name"]=>
@@ -49,9 +51,11 @@ array(3) {
     int(1)
   }
   ["file2"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file2.txt"
+    ["full_path"]=>
+    string(9) "file2.txt"
     ["type"]=>
     string(0) ""
     ["tmp_name"]=>
@@ -62,9 +66,11 @@ array(3) {
     int(0)
   }
   ["file3"]=>
-  array(5) {
+  array(6) {
     ["name"]=>
     string(9) "file3.txt"
+    ["full_path"]=>
+    string(9) "file3.txt"
     ["type"]=>
     string(16) "text/plain-file3"
     ["tmp_name"]=>
diff --git a/tests/lang/bug28213.phpt b/tests/lang/bug28213.phpt
index 3677d4c6f3557..abd965cbb935a 100644
--- a/tests/lang/bug28213.phpt
+++ b/tests/lang/bug28213.phpt
@@ -6,5 +6,8 @@ class FooBar { static function error() { debug_print_backtrace(); } }
 set_error_handler(array('FooBar', 'error'));
 include('foobar.php');
 ?>
---EXPECTREGEX--
-.*#1\s*include.*
+--EXPECTF--
+#0 %s(%d): FooBar::error(2, 'include(foobar....', '%s', 4)
+#1 %s(%d): include()
+#0 %s(%d): FooBar::error(2, 'include(): Fail...', '%s', 4)
+#1 %s(%d): include()