xref: /PHP-8.3/ext/opcache/jit/zend_jit_x86.dasc (revision dc0987d1)
1/*
2 *  +----------------------------------------------------------------------+
3 *  | Zend JIT                                                             |
4 *  +----------------------------------------------------------------------+
5 *  | Copyright (c) The PHP Group                                          |
6 *  +----------------------------------------------------------------------+
7 *  | This source file is subject to version 3.01 of the PHP license,      |
8 *  | that is bundled with this package in the file LICENSE, and is        |
9 *  | available through the world-wide-web at the following url:           |
10 *  | https://www.php.net/license/3_01.txt                                 |
11 *  | If you did not receive a copy of the PHP license and are unable to   |
12 *  | obtain it through the world-wide-web, please send a note to          |
13 *  | license@php.net so we can mail you a copy immediately.               |
14 *  +----------------------------------------------------------------------+
15 *  | Authors: Dmitry Stogov <dmitry@php.net>                              |
16 *  |          Xinchen Hui <laruence@php.net>                              |
17 *  +----------------------------------------------------------------------+
18 */
19
20|.if X64
21 |.arch x64
22|.else
23 |.arch x86
24|.endif
25
26|.if X64WIN
27 |.define FP,      r14
28 |.define IP,      r15
29 |.define IPl,     r15d
30 |.define RX,      r15       // the same as VM IP reused as a general purpose reg
31 |.define CARG1,   rcx       // x64/POSIX C call arguments.
32 |.define CARG2,   rdx
33 |.define CARG3,   r8
34 |.define CARG4,   r9
35 |.define CARG1d,  ecx
36 |.define CARG2d,  edx
37 |.define CARG3d,  r8d
38 |.define CARG4d,  r9d
39 |.define FCARG1a, CARG1     // Simulate x86 fastcall.
40 |.define FCARG2a, CARG2
41 |.define FCARG1d, CARG1d
42 |.define FCARG2d, CARG2d
43 |.define SPAD,    0x58      // padding for CPU stack alignment
44 |.define NR_SPAD, 0x58      // padding for CPU stack alignment
45 |.define T3,      [r4+0x50] // Used to store old value of IP
46 |.define T2,      [r4+0x48] // Used to store old value of FP
47 |.define T1,      [r4+0x40]
48 |.define A6,      [r4+0x28] // preallocated slot for 6-th argument
49 |.define A5,      [r4+0x20] // preallocated slot for 5-th argument
50|.elif X64
51 |.define FP,      r14
52 |.define IP,      r15
53 |.define IPl,     r15d
54 |.define RX,      r15       // the same as VM IP reused as a general purpose reg
55 |.define CARG1,   rdi       // x64/POSIX C call arguments.
56 |.define CARG2,   rsi
57 |.define CARG3,   rdx
58 |.define CARG4,   rcx
59 |.define CARG5,   r8
60 |.define CARG6,   r9
61 |.define CARG1d,  edi
62 |.define CARG2d,  esi
63 |.define CARG3d,  edx
64 |.define CARG4d,  ecx
65 |.define CARG5d,  r8d
66 |.define CARG6d,  r9d
67 |.define FCARG1a, CARG1     // Simulate x86 fastcall.
68 |.define FCARG2a, CARG2
69 |.define FCARG1d, CARG1d
70 |.define FCARG2d, CARG2d
71 |.define SPAD,    0x18      // padding for CPU stack alignment
72 |.define NR_SPAD, 0x28      // padding for CPU stack alignment
73 |.define T3,      [r4+0x20] // Used to store old value of IP (CALL VM only)
74 |.define T2,      [r4+0x18] // Used to store old value of FP (CALL VM only)
75 |.define T1,      [r4]
76|.else
77 |.define FP,      esi
78 |.define IP,      edi
79 |.define IPl,     edi
80 |.define RX,      edi       // the same as VM IP reused as a general purpose reg
81 |.define FCARG1a, ecx       // x86 fastcall arguments.
82 |.define FCARG2a, edx
83 |.define FCARG1d, ecx
84 |.define FCARG2d, edx
85 |.define SPAD,    0x1c      // padding for CPU stack alignment
86 |.define NR_SPAD, 0x1c      // padding for CPU stack alignment
87 |.define T3,      [r4+0x18] // Used to store old value of IP (CALL VM only)
88 |.define T2,      [r4+0x14] // Used to store old value of FP (CALL VM only)
89 |.define T1,      [r4]
90 |.define A4,      [r4+0xC]  // preallocated slots for arguments of "cdecl" functions (intersect with T1)
91 |.define A3,      [r4+0x8]
92 |.define A2,      [r4+0x4]
93 |.define A1,      [r4]
94|.endif
95
96|.define HYBRID_SPAD, 16     // padding for stack alignment
97
98#ifdef _WIN64
99# define TMP_ZVAL_OFFSET 0x20
100#else
101# define TMP_ZVAL_OFFSET 0
102#endif
103
104#define DASM_ALIGNMENT 16
105
106/* According to x86 and x86_64 ABI, CPU stack has to be 16 byte aligned to
107 * guarantee proper alignment of 128-bit SSE data allocated on stack.
108 * With broken alignment any execution of SSE code, including calls to
109 * memcpy() and others, may lead to crash.
110 */
111
112const char* zend_reg_name[] = {
113#if defined(__x86_64__) || defined(_M_X64)
114	"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
115	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
116	"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
117	"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
118#else
119	"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
120	"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
121#endif
122};
123
124/* Simulate x86 fastcall */
125#ifdef _WIN64
126# define ZREG_FCARG1 ZREG_RCX
127# define ZREG_FCARG2 ZREG_RDX
128#elif defined(__x86_64__)
129# define ZREG_FCARG1 ZREG_RDI
130# define ZREG_FCARG2 ZREG_RSI
131#else
132# define ZREG_FCARG1 ZREG_RCX
133# define ZREG_FCARG2 ZREG_RDX
134#endif
135
136|.type EX, zend_execute_data, FP
137|.type OP, zend_op
138|.type ZVAL, zval
139|.actionlist dasm_actions
140|.globals zend_lb
141|.section code, cold_code, jmp_table
142
143static void* dasm_labels[zend_lb_MAX];
144
145#if ZTS
146static size_t tsrm_ls_cache_tcb_offset = 0;
147static size_t tsrm_tls_index;
148static size_t tsrm_tls_offset;
149#endif
150
151#define IS_32BIT(addr) (((uintptr_t)(addr)) <= 0x7fffffff)
152
153#define IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1)))
154
155/* Call range is before or after 2GB */
156#define MAY_USE_32BIT_ADDR(addr) \
157	(IS_SIGNED_32BIT((char*)(addr) - (char*)dasm_buf) && \
158	IS_SIGNED_32BIT((char*)(addr) - (char*)dasm_end))
159
160#define CAN_USE_AVX() (JIT_G(opt_flags) & allowed_opt_flags & ZEND_JIT_CPU_AVX)
161
162/* Not Implemented Yet */
163|.macro NIY
164||	//ZEND_ASSERT(0);
165|	int3
166|.endmacro
167
168|.macro NIY_STUB
169||	//ZEND_ASSERT(0);
170|	int3
171|.endmacro
172
173|.macro ADD_HYBRID_SPAD
174||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
175|		add r4, HYBRID_SPAD
176||#endif
177|.endmacro
178
179|.macro SUB_HYBRID_SPAD
180||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
181|		sub r4, HYBRID_SPAD
182||#endif
183|.endmacro
184
185|.macro LOAD_ADDR, reg, addr
186|	.if X64
187||		if (IS_SIGNED_32BIT(addr)) {
188|			mov reg, ((ptrdiff_t)addr)    // 0x48 0xc7 0xc0 <imm-32-bit>
189||		} else {
190|			mov64 reg, ((ptrdiff_t)addr)  // 0x48 0xb8 <imm-64-bit>
191||		}
192|	.else
193|		mov reg, ((ptrdiff_t)addr)
194|	.endif
195|.endmacro
196
197|.macro LOAD_TSRM_CACHE, reg
198|	.if X64WIN
199|		gs
200|		mov reg, aword [0x58]
201|		mov reg, aword [reg+tsrm_tls_index]
202|		mov reg, aword [reg+tsrm_tls_offset]
203|	.elif WIN
204|		fs
205|		mov reg, aword [0x2c]
206|		mov reg, aword [reg+tsrm_tls_index]
207|		mov reg, aword [reg+tsrm_tls_offset]
208|	.elif X64APPLE
209|		gs
210||		if (tsrm_ls_cache_tcb_offset) {
211|			mov reg, aword [tsrm_ls_cache_tcb_offset]
212||		} else {
213|			mov reg, aword [tsrm_tls_index]
214|			mov reg, aword [reg+tsrm_tls_offset]
215||		}
216|	.elif X64
217|		fs
218||		if (tsrm_ls_cache_tcb_offset) {
219|			mov reg, aword [tsrm_ls_cache_tcb_offset]
220||		} else {
221|			mov reg, [0x8]
222|			mov reg, aword [reg+tsrm_tls_index]
223|			mov reg, aword [reg+tsrm_tls_offset]
224||		}
225|	.else
226|		gs
227||		if (tsrm_ls_cache_tcb_offset) {
228|			mov reg, aword [tsrm_ls_cache_tcb_offset]
229||		} else {
230|			mov reg, [0x4]
231|			mov reg, aword [reg+tsrm_tls_index]
232|			mov reg, aword [reg+tsrm_tls_offset]
233||		}
234|	.endif
235|.endmacro
236
237|.macro LOAD_ADDR_ZTS, reg, struct, field
238|	.if ZTS
239|		LOAD_TSRM_CACHE reg
240|		lea reg, aword [reg + (struct.._offset + offsetof(zend_..struct, field))]
241|	.else
242|		LOAD_ADDR reg, &struct.field
243|	.endif
244|.endmacro
245
246|.macro PUSH_ADDR, addr, tmp_reg
247|	.if X64
248||		if (IS_SIGNED_32BIT(addr)) {
249|			push ((ptrdiff_t)addr)
250||		} else {
251|			mov64 tmp_reg, ((ptrdiff_t)addr)
252|			push tmp_reg
253||		}
254|	.else
255|		push ((ptrdiff_t)addr)
256|	.endif
257|.endmacro
258
259|.macro ADDR_STORE, mem, addr, tmp_reg
260|	.if X64
261||		if (IS_SIGNED_32BIT(addr)) {
262|			mov mem, ((ptrdiff_t)addr)
263||		} else {
264|			mov64 tmp_reg, ((ptrdiff_t)addr)
265|			mov mem, tmp_reg
266||		}
267|	.else
268|		mov mem, ((ptrdiff_t)addr)
269|	.endif
270|.endmacro
271
272|.macro ADDR_CMP, mem, addr, tmp_reg
273|	.if X64
274||		if (IS_SIGNED_32BIT(addr)) {
275|			cmp mem, ((ptrdiff_t)addr)
276||		} else {
277|			mov64 tmp_reg, ((ptrdiff_t)addr)
278|			cmp mem, tmp_reg
279||		}
280|	.else
281|		cmp mem, ((ptrdiff_t)addr)
282|	.endif
283|.endmacro
284
285|.macro PUSH_ADDR_ZTS, struct, field, tmp_reg
286|	.if ZTS
287|		LOAD_TSRM_CACHE tmp_reg
288|		lea tmp_reg, aword [tmp_reg + (struct.._offset + offsetof(zend_..struct, field))]
289|		push tmp_reg
290|	.else
291|		PUSH_ADDR &struct.field, tmp_reg
292|	.endif
293|.endmacro
294
295|.macro _MEM_OP, mem_ins, prefix, addr, op2, tmp_reg
296|	.if X64
297||		if (IS_SIGNED_32BIT(addr)) {
298|			mem_ins prefix [addr], op2
299||		} else {
300|			mov64 tmp_reg, ((ptrdiff_t)addr)
301|			mem_ins prefix [tmp_reg], op2
302||		}
303|	.else
304|		mem_ins prefix [addr], op2
305|	.endif
306|.endmacro
307
308|.macro MEM_LOAD_OP, mem_ins, reg, prefix, addr, tmp_reg
309|	.if X64
310||		if (IS_SIGNED_32BIT(addr)) {
311|			mem_ins reg, prefix [addr]
312||		} else {
313|			mov64 tmp_reg, ((ptrdiff_t)addr)
314|			mem_ins reg, prefix [tmp_reg]
315||		}
316|	.else
317|		mem_ins reg, prefix [addr]
318|	.endif
319|.endmacro
320
321|.macro MEM_LOAD, op1, prefix, addr, tmp_reg
322|	MEM_LOAD_OP mov, op1, prefix, addr, tmp_reg
323|.endmacro
324
325|.macro _MEM_OP_ZTS, mem_ins, prefix, struct, field, op2, tmp_reg
326|	.if ZTS
327|		LOAD_TSRM_CACHE tmp_reg
328|		mem_ins prefix [tmp_reg+(struct.._offset+offsetof(zend_..struct, field))], op2
329|	.else
330|		_MEM_OP mem_ins, prefix, &struct.field, op2, tmp_reg
331|	.endif
332|.endmacro
333
334|.macro MEM_STORE_ZTS, prefix, struct, field, op2, tmp_reg
335|	_MEM_OP_ZTS mov, prefix, struct, field, op2, tmp_reg
336|.endmacro
337
338|.macro MEM_CMP_ZTS, prefix, struct, field, op2, tmp_reg
339|	_MEM_OP_ZTS cmp, prefix, struct, field, op2, tmp_reg
340|.endmacro
341
342|.macro MEM_UPDATE_ZTS, mem_ins, prefix, struct, field, op2, tmp_reg
343|	_MEM_OP_ZTS mem_ins, prefix, struct, field, op2, tmp_reg
344|.endmacro
345
346|.macro MEM_LOAD_OP_ZTS, mem_ins, reg, prefix, struct, field, tmp_reg
347|	.if ZTS
348|		LOAD_TSRM_CACHE tmp_reg
349|		mem_ins reg, prefix [tmp_reg+(struct.._offset+offsetof(zend_..struct, field))]
350|	.else
351|		MEM_LOAD_OP mem_ins, reg, prefix, &struct.field, tmp_reg
352|	.endif
353|.endmacro
354
355|.macro MEM_LOAD_ZTS, reg, prefix, struct, field, tmp_reg
356|	MEM_LOAD_OP_ZTS mov, reg, prefix, struct, field, tmp_reg
357|.endmacro
358
359|.macro EXT_CALL, func, tmp_reg
360|	.if X64
361||		if (MAY_USE_32BIT_ADDR(func)) {
362|			call qword &func
363||		} else {
364|			LOAD_ADDR tmp_reg, func
365|			call tmp_reg
366||		}
367|	.else
368|		call dword &func
369|	.endif
370|.endmacro
371
372|.macro EXT_JMP, func, tmp_reg
373|	.if X64
374||		if (MAY_USE_32BIT_ADDR(func)) {
375|			jmp qword &func
376||		} else {
377|			LOAD_ADDR tmp_reg, func
378|			jmp tmp_reg
379||		}
380|	.else
381|		jmp dword &func
382|	.endif
383|.endmacro
384
385|.macro SAVE_IP
386||	if (GCC_GLOBAL_REGS) {
387|		mov aword EX->opline, IP
388||	}
389|.endmacro
390
391|.macro LOAD_IP
392||	if (GCC_GLOBAL_REGS) {
393|		mov IP, aword EX->opline
394||	}
395|.endmacro
396
397|.macro LOAD_IP_ADDR, addr
398||	if (GCC_GLOBAL_REGS) {
399|		LOAD_ADDR IP, addr
400||	} else {
401|		ADDR_STORE aword EX->opline, addr, RX
402||	}
403|.endmacro
404
405|.macro LOAD_IP_ADDR_ZTS, struct, field
406|	.if ZTS
407||		if (GCC_GLOBAL_REGS) {
408|			LOAD_TSRM_CACHE IP
409|			lea IP, aword [IP + (struct.._offset + offsetof(zend_..struct, field))]
410||		} else {
411|			LOAD_TSRM_CACHE RX
412|			lea RX, aword [RX + (struct.._offset + offsetof(zend_..struct, field))]
413|			mov aword EX->opline, RX
414||		}
415|	.else
416|		LOAD_IP_ADDR &struct.field
417|	.endif
418|.endmacro
419
420|.macro GET_IP, reg
421||	if (GCC_GLOBAL_REGS) {
422|		mov reg, IP
423||	} else {
424|		mov reg, aword EX->opline
425||	}
426|.endmacro
427
428|.macro ADD_IP, val
429||	if (GCC_GLOBAL_REGS) {
430|		add IP, val
431||	} else {
432|		add aword EX->opline, val
433||	}
434|.endmacro
435
436|.macro JMP_IP
437||	if (GCC_GLOBAL_REGS) {
438|		jmp aword [IP]
439||	} else {
440|		mov r0, aword EX:FCARG1a->opline
441|		jmp aword [r0]
442||	}
443|.endmacro
444
445/* In 64-bit build we compare only low 32-bits.
446 * x86_64 cmp instruction doesn't support immediate 64-bit operand, and full
447 * comparison would require an additional load of 64-bit address into register.
448 * This is not a problem at all, while JIT buffer size is less than 4GB.
449 */
450|.macro CMP_IP, addr
451||	if (GCC_GLOBAL_REGS) {
452|		cmp IPl, addr
453||	} else {
454|		cmp dword EX->opline, addr
455||	}
456|.endmacro
457
458|.macro LOAD_ZVAL_ADDR, reg, addr
459||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
460|		LOAD_ADDR reg, Z_ZV(addr)
461||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
462||		if (Z_OFFSET(addr)) {
463|			lea reg, qword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
464||		} else {
465|			mov reg, Ra(Z_REG(addr))
466||		}
467||	} else {
468||		ZEND_UNREACHABLE();
469||	}
470|.endmacro
471
472|.macro PUSH_ZVAL_ADDR, addr, tmp_reg
473||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
474|		PUSH_ADDR Z_ZV(addr), tmp_reg
475||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
476||		if (Z_OFFSET(addr)) {
477|			lea tmp_reg, qword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
478|			push tmp_reg
479||		} else {
480|			push Ra(Z_REG(addr))
481||		}
482||	} else {
483||		ZEND_UNREACHABLE();
484||	}
485|.endmacro
486
487|.macro GET_Z_TYPE_INFO, reg, zv
488|	mov reg, dword [zv+offsetof(zval,u1.type_info)]
489|.endmacro
490
491|.macro SET_Z_TYPE_INFO, zv, type
492|	mov dword [zv+offsetof(zval,u1.type_info)], type
493|.endmacro
494
495|.macro GET_ZVAL_TYPE, reg, addr
496||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
497|	mov reg, byte [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval,u1.v.type)]
498|.endmacro
499
500|.macro GET_ZVAL_TYPE_INFO, reg, addr
501||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
502|	mov reg, dword [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval,u1.type_info)]
503|.endmacro
504
505|.macro SET_ZVAL_TYPE_INFO, addr, type
506||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
507|	mov dword [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval,u1.type_info)], type
508|.endmacro
509
510|.macro GET_Z_PTR, reg, zv
511|	mov reg, aword [zv]
512|.endmacro
513
514|.macro GET_Z_W2, reg, zv
515|	mov reg, dword [zv+4]
516|.endmacro
517
518|.macro SET_Z_W2, zv, reg
519|	mov dword [zv+4], reg
520|.endmacro
521
522|.macro GET_ZVAL_PTR, reg, addr
523||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
524|	mov reg, aword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
525|.endmacro
526
527|.macro SET_ZVAL_PTR, addr, val
528||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
529|	mov aword [Ra(Z_REG(addr))+Z_OFFSET(addr)], val
530|.endmacro
531
532|.macro GET_ZVAL_W2, reg, addr
533||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
534|	mov reg, dword [Ra(Z_REG(addr))+Z_OFFSET(addr)+4]
535|.endmacro
536
537|.macro SET_ZVAL_W2, addr, val
538||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
539|	mov dword [Ra(Z_REG(addr))+Z_OFFSET(addr)+4], val
540|.endmacro
541
542|.macro UNDEF_OPLINE_RESULT
543|	mov r0, EX->opline
544|	mov eax, dword OP:r0->result.var
545|	SET_Z_TYPE_INFO FP + r0, IS_UNDEF
546|.endmacro
547
548|.macro UNDEF_OPLINE_RESULT_IF_USED
549|	test byte OP:RX->result_type, (IS_TMP_VAR|IS_VAR)
550|	jz >1
551|	mov eax, dword OP:RX->result.var
552|	SET_Z_TYPE_INFO FP + r0, IS_UNDEF
553|1:
554|.endmacro
555
556|.macro SSE_AVX_INS, sse_ins, avx_ins, op1, op2
557||	if (CAN_USE_AVX()) {
558|		avx_ins op1, op2
559||	} else {
560|		sse_ins op1, op2
561||	}
562|.endmacro
563
564|.macro SSE_OP, sse_ins, reg, addr, tmp_reg
565||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
566|		MEM_LOAD_OP sse_ins, xmm(reg-ZREG_XMM0), qword, Z_ZV(addr), tmp_reg
567||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
568|		sse_ins xmm(reg-ZREG_XMM0), qword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
569||	} else if (Z_MODE(addr) == IS_REG) {
570|		sse_ins xmm(reg-ZREG_XMM0), xmm(Z_REG(addr)-ZREG_XMM0)
571||	} else {
572||		ZEND_UNREACHABLE();
573||	}
574|.endmacro
575
576|.macro DOUBLE_CMP, reg, addr
577||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
578|		.if X64
579||			if (IS_SIGNED_32BIT(Z_ZV(addr))) {
580|				SSE_AVX_INS ucomisd, vucomisd, xmm(reg-ZREG_XMM0), qword [Z_ZV(addr)]
581||			} else {
582|				LOAD_ADDR r0, Z_ZV(addr)
583|				SSE_AVX_INS ucomisd, vucomisd, xmm(reg-ZREG_XMM0), qword [r0]
584||			}
585|		.else
586|			SSE_AVX_INS ucomisd, vucomisd, xmm(reg-ZREG_XMM0), qword [Z_ZV(addr)]
587|		.endif
588||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
589|		SSE_AVX_INS ucomisd, vucomisd, xmm(reg-ZREG_XMM0), qword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
590||	} else if (Z_MODE(addr) == IS_REG) {
591|		SSE_AVX_INS ucomisd, vucomisd, xmm(reg-ZREG_XMM0), xmm(Z_REG(addr)-ZREG_XMM0)
592||	} else {
593||		ZEND_UNREACHABLE();
594||	}
595|.endmacro
596
597|.macro DOUBLE_GET_LONG, reg, lval, tmp_reg
598||		if (lval == 0) {
599||			if (CAN_USE_AVX()) {
600|				vxorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
601||			} else {
602|				xorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
603||			}
604||		} else {
605|.if X64
606||			if (!IS_SIGNED_32BIT(lval)) {
607|				mov64 Ra(tmp_reg), lval
608||			} else {
609|				mov Ra(tmp_reg), lval
610||			}
611|.else
612|			mov Ra(tmp_reg), lval
613|.endif
614||			if (CAN_USE_AVX()) {
615|				vxorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
616|				vcvtsi2sd, xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0), Ra(tmp_reg)
617||			} else {
618|				xorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
619|				cvtsi2sd, xmm(reg-ZREG_XMM0), Ra(tmp_reg)
620||			}
621||		}
622|.endmacro
623
624|.macro DOUBLE_GET_ZVAL_LVAL, reg, addr, tmp_reg
625||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
626|		DOUBLE_GET_LONG reg, Z_LVAL_P(Z_ZV(addr)), tmp_reg
627||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
628||		if (CAN_USE_AVX()) {
629|			vxorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
630|			vcvtsi2sd xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0), aword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
631||		} else {
632|			xorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
633|			cvtsi2sd xmm(reg-ZREG_XMM0), aword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
634||		}
635||	} else if (Z_MODE(addr) == IS_REG) {
636||		if (CAN_USE_AVX()) {
637|			vxorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
638|			vcvtsi2sd xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0), Ra(Z_REG(addr))
639||		} else {
640|			xorps xmm(reg-ZREG_XMM0), xmm(reg-ZREG_XMM0)
641|			cvtsi2sd xmm(reg-ZREG_XMM0), Ra(Z_REG(addr))
642||		}
643||	} else {
644||		ZEND_UNREACHABLE();
645||	}
646|.endmacro
647
648|.macro DOUBLE_GET_ZVAL_DVAL, reg, addr
649||	if (Z_MODE(addr) != IS_REG || reg != Z_REG(addr)) {
650||		if (Z_MODE(addr) == IS_CONST_ZVAL) {
651|			.if X64
652||				if (IS_SIGNED_32BIT(Z_ZV(addr))) {
653|					SSE_AVX_INS movsd, vmovsd, xmm(reg-ZREG_XMM0), qword [Z_ZV(addr)]
654||				} else {
655|					LOAD_ADDR r0, Z_ZV(addr)
656|					SSE_AVX_INS movsd, vmovsd, xmm(reg-ZREG_XMM0), qword [r0]
657||				}
658|			.else
659|				SSE_AVX_INS movsd, vmovsd, xmm(reg-ZREG_XMM0), qword [Z_ZV(addr)]
660|			.endif
661||		} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
662|			SSE_AVX_INS movsd, vmovsd, xmm(reg-ZREG_XMM0), qword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
663||		} else if (Z_MODE(addr) == IS_REG) {
664|			SSE_AVX_INS movaps, vmovaps, xmm(reg-ZREG_XMM0), xmm(Z_REG(addr)-ZREG_XMM0)
665||		} else {
666||			ZEND_UNREACHABLE();
667||		}
668||	}
669|.endmacro
670
671|.macro SSE_MATH, opcode, reg, addr, tmp_reg
672||	switch (opcode) {
673||		case ZEND_ADD:
674|			SSE_OP addsd, reg, addr, tmp_reg
675||			break;
676||		case ZEND_SUB:
677|			SSE_OP subsd, reg, addr, tmp_reg
678||			break;
679||		case ZEND_MUL:
680|			SSE_OP mulsd, reg, addr, tmp_reg
681||			break;
682||		case ZEND_DIV:
683|			SSE_OP divsd, reg, addr, tmp_reg
684||			break;
685||	}
686|.endmacro
687
688|.macro SSE_MATH_REG, opcode, dst_reg, src_reg
689||	switch (opcode) {
690||		case ZEND_ADD:
691|			addsd xmm(dst_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
692||			break;
693||		case ZEND_SUB:
694|			subsd xmm(dst_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
695||			break;
696||		case ZEND_MUL:
697|			mulsd xmm(dst_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
698||			break;
699||		case ZEND_DIV:
700|			divsd xmm(dst_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
701||			break;
702||	}
703|.endmacro
704
705|.macro DOUBLE_SET_ZVAL_DVAL, addr, reg
706||	if (Z_MODE(addr) == IS_REG) {
707||		if (reg != Z_REG(addr)) {
708|			SSE_AVX_INS movaps, vmovaps, xmm(Z_REG(addr)-ZREG_XMM0), xmm(reg-ZREG_XMM0)
709||		}
710||	} else {
711||		ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
712|		SSE_AVX_INS movsd, vmovsd, qword [Ra(Z_REG(addr))+Z_OFFSET(addr)], xmm(reg-ZREG_XMM0)
713||	}
714|.endmacro
715
716|.macro AVX_OP, avx_ins, reg, op1_reg, addr, tmp_reg
717||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
718|		.if X64
719||			if (IS_SIGNED_32BIT(Z_ZV(addr))) {
720|				avx_ins xmm(reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), qword [Z_ZV(addr)]
721||			} else {
722|				mov64 tmp_reg, ((ptrdiff_t)Z_ZV(addr))
723|				avx_ins xmm(reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), qword [tmp_reg]
724||			}
725|		.else
726|			avx_ins xmm(reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), qword [addr]
727|		.endif
728||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
729|		avx_ins xmm(reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), qword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
730||	} else if (Z_MODE(addr) == IS_REG) {
731|		avx_ins xmm(reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), xmm(Z_REG(addr)-ZREG_XMM0)
732||	} else {
733||		ZEND_UNREACHABLE();
734||	}
735|.endmacro
736
737|.macro AVX_MATH, opcode, reg, op1_reg, addr, tmp_reg
738||	switch (opcode) {
739||		case ZEND_ADD:
740|			AVX_OP vaddsd, reg, op1_reg, addr, tmp_reg
741||			break;
742||		case ZEND_SUB:
743|			AVX_OP vsubsd, reg, op1_reg, addr, tmp_reg
744||			break;
745||		case ZEND_MUL:
746|			AVX_OP vmulsd, reg, op1_reg, addr, tmp_reg
747||			break;
748||		case ZEND_DIV:
749|			AVX_OP vdivsd, reg, op1_reg, addr, tmp_reg
750||			break;
751||	}
752|.endmacro
753
754|.macro AVX_MATH_REG, opcode, dst_reg, op1_reg, src_reg
755||	switch (opcode) {
756||		case ZEND_ADD:
757|			vaddsd xmm(dst_reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
758||			break;
759||		case ZEND_SUB:
760|			vsubsd xmm(dst_reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
761||			break;
762||		case ZEND_MUL:
763|			vmulsd xmm(dst_reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
764||			break;
765||		case ZEND_DIV:
766|			vdivsd xmm(dst_reg-ZREG_XMM0), xmm(op1_reg-ZREG_XMM0), xmm(src_reg-ZREG_XMM0)
767||			break;
768||	}
769|.endmacro
770
771|.macro LONG_OP, long_ins, reg, addr, tmp_reg
772||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
773|		.if X64
774||			if (!IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(addr)))) {
775|				mov64 tmp_reg, Z_LVAL_P(Z_ZV(addr))
776|				long_ins Ra(reg), tmp_reg
777||			} else {
778|				long_ins Ra(reg), Z_LVAL_P(Z_ZV(addr))
779||			}
780|		.else
781|			long_ins Ra(reg), Z_LVAL_P(Z_ZV(addr))
782|		.endif
783||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
784|		long_ins Ra(reg), aword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
785||	} else if (Z_MODE(addr) == IS_REG) {
786|		long_ins Ra(reg), Ra(Z_REG(addr))
787||	} else {
788||		ZEND_UNREACHABLE();
789||	}
790|.endmacro
791
792|.macro LONG_OP_WITH_32BIT_CONST, long_ins, op1_addr, lval
793||	if (Z_MODE(op1_addr) == IS_MEM_ZVAL) {
794|		long_ins aword [Ra(Z_REG(op1_addr))+Z_OFFSET(op1_addr)], lval
795||	} else if (Z_MODE(op1_addr) == IS_REG) {
796|		long_ins Ra(Z_REG(op1_addr)), lval
797||	} else {
798||		ZEND_UNREACHABLE();
799||	}
800|.endmacro
801
802|.macro LONG_OP_WITH_CONST, long_ins, op1_addr, lval
803||	if (Z_MODE(op1_addr) == IS_MEM_ZVAL) {
804|	   .if X64
805||			if (!IS_SIGNED_32BIT(lval)) {
806|				mov64 r0, lval
807|				long_ins aword [Ra(Z_REG(op1_addr))+Z_OFFSET(op1_addr)], r0
808||			} else {
809|				long_ins aword [Ra(Z_REG(op1_addr))+Z_OFFSET(op1_addr)], lval
810||			}
811|		.else
812|			long_ins aword [Ra(Z_REG(op1_addr))+Z_OFFSET(op1_addr)], lval
813|		.endif
814||	} else if (Z_MODE(op1_addr) == IS_REG) {
815|	   .if X64
816||			if (!IS_SIGNED_32BIT(lval)) {
817|				mov64 r0, lval
818|				long_ins Ra(Z_REG(op1_addr)), r0
819||			} else {
820|				long_ins Ra(Z_REG(op1_addr)), lval
821||			}
822|		.else
823|			long_ins Ra(Z_REG(op1_addr)), lval
824|		.endif
825||	} else {
826||		ZEND_UNREACHABLE();
827||	}
828|.endmacro
829
830|.macro GET_ZVAL_LVAL, reg, addr
831||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
832||		if (Z_LVAL_P(Z_ZV(addr)) == 0) {
833|			xor Ra(reg), Ra(reg)
834||		} else {
835|			.if X64
836||				if (!IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(addr)))) {
837|					mov64 Ra(reg), Z_LVAL_P(Z_ZV(addr))
838||				} else {
839|					mov Ra(reg), Z_LVAL_P(Z_ZV(addr))
840||				}
841|			.else
842|				mov Ra(reg), Z_LVAL_P(Z_ZV(addr))
843|			.endif
844||		}
845||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
846|		mov Ra(reg), aword [Ra(Z_REG(addr))+Z_OFFSET(addr)]
847||	} else if (Z_MODE(addr) == IS_REG) {
848||		if (reg != Z_REG(addr)) {
849|			mov Ra(reg), Ra(Z_REG(addr))
850||		}
851||	} else {
852||		ZEND_UNREACHABLE();
853||	}
854|.endmacro
855
856|.macro LONG_MATH, opcode, reg, addr, tmp_reg
857||	switch (opcode) {
858||		case ZEND_ADD:
859|			LONG_OP add, reg, addr, Ra(tmp_reg)
860||			break;
861||		case ZEND_SUB:
862|			LONG_OP sub, reg, addr, Ra(tmp_reg)
863||			break;
864||		case ZEND_MUL:
865|			LONG_OP imul, reg, addr, Ra(tmp_reg)
866||			break;
867||		case ZEND_BW_OR:
868|			LONG_OP or, reg, addr, Ra(tmp_reg)
869||			break;
870||		case ZEND_BW_AND:
871|			LONG_OP and, reg, addr, Ra(tmp_reg)
872||			break;
873||		case ZEND_BW_XOR:
874|			LONG_OP xor, reg, addr, Ra(tmp_reg)
875||			break;
876||		default:
877||			ZEND_UNREACHABLE();
878||	}
879|.endmacro
880
881|.macro LONG_MATH_REG, opcode, dst_reg, src_reg
882||	switch (opcode) {
883||		case ZEND_ADD:
884|			add dst_reg, src_reg
885||			break;
886||		case ZEND_SUB:
887|			sub dst_reg, src_reg
888||			break;
889||		case ZEND_MUL:
890|			imul dst_reg, src_reg
891||			break;
892||		case ZEND_BW_OR:
893|			or dst_reg, src_reg
894||			break;
895||		case ZEND_BW_AND:
896|			and dst_reg, src_reg
897||			break;
898||		case ZEND_BW_XOR:
899|			xor dst_reg, src_reg
900||			break;
901||		default:
902||			ZEND_UNREACHABLE();
903||	}
904|.endmacro
905
906|.macro SET_ZVAL_LVAL, addr, lval
907||	if (Z_MODE(addr) == IS_REG) {
908|		mov Ra(Z_REG(addr)), lval
909||	} else {
910||		ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
911|		mov aword [Ra(Z_REG(addr))+Z_OFFSET(addr)], lval
912||	}
913|.endmacro
914
915|.macro ZVAL_COPY_CONST, dst_addr, dst_info, dst_def_info, zv, tmp_reg
916||	if (Z_TYPE_P(zv) > IS_TRUE) {
917||		if (Z_TYPE_P(zv) == IS_DOUBLE) {
918||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : ZREG_XMM0;
919||			if (Z_DVAL_P(zv) == 0.0 && !is_signed(Z_DVAL_P(zv))) {
920||				if (CAN_USE_AVX()) {
921|					vxorps xmm(dst_reg-ZREG_XMM0), xmm(dst_reg-ZREG_XMM0), xmm(dst_reg-ZREG_XMM0)
922||				} else {
923|					xorps xmm(dst_reg-ZREG_XMM0), xmm(dst_reg-ZREG_XMM0)
924||				}
925|			.if X64
926||			} else if (!IS_SIGNED_32BIT(zv)) {
927|				mov64 Ra(tmp_reg), ((uintptr_t)zv)
928|				SSE_AVX_INS movsd, vmovsd, xmm(dst_reg-ZREG_XMM0), qword [Ra(tmp_reg)]
929|			.endif
930||			} else {
931|				SSE_AVX_INS movsd, vmovsd, xmm(dst_reg-ZREG_XMM0), qword [((uint32_t)(uintptr_t)zv)]
932||			}
933|			DOUBLE_SET_ZVAL_DVAL dst_addr, dst_reg
934||		} else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) {
935||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : ZREG_XMM0;
936|			DOUBLE_GET_LONG dst_reg, Z_LVAL_P(zv), ZREG_R0
937|			DOUBLE_SET_ZVAL_DVAL dst_addr, dst_reg
938||		} else if (Z_LVAL_P(zv) == 0 && Z_MODE(dst_addr) == IS_REG) {
939|			xor Ra(Z_REG(dst_addr)), Ra(Z_REG(dst_addr))
940||		} else {
941|			.if X64
942||				if (!IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
943||					if (Z_MODE(dst_addr) == IS_REG) {
944|						mov64 Ra(Z_REG(dst_addr)), ((uintptr_t)Z_LVAL_P(zv))
945||					} else {
946|						mov64 Ra(tmp_reg), ((uintptr_t)Z_LVAL_P(zv))
947|						SET_ZVAL_LVAL dst_addr, Ra(tmp_reg)
948||					}
949||				} else {
950|					SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv)
951||				}
952|			.else
953|				SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv)
954|			.endif
955||		}
956||	}
957||	if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
958||		if (dst_def_info == MAY_BE_DOUBLE) {
959||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
960|				SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE
961||			}
962||		} 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) {
963|			SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv)
964||		}
965||	}
966|.endmacro
967
968|.macro ZVAL_COPY_CONST_2, dst_addr, res_addr, dst_info, dst_def_info, zv, tmp_reg
969||	if (Z_TYPE_P(zv) > IS_TRUE) {
970||		if (Z_TYPE_P(zv) == IS_DOUBLE) {
971||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ?
972||				Z_REG(dst_addr) : ((Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : ZREG_XMM0);
973||			if (Z_DVAL_P(zv) == 0.0 && !is_signed(Z_DVAL_P(zv))) {
974||				if (CAN_USE_AVX()) {
975|					vxorps xmm(dst_reg-ZREG_XMM0), xmm(dst_reg-ZREG_XMM0), xmm(dst_reg-ZREG_XMM0)
976||				} else {
977|					xorps xmm(dst_reg-ZREG_XMM0), xmm(dst_reg-ZREG_XMM0)
978||				}
979|			.if X64
980||			} else if (!IS_SIGNED_32BIT(zv)) {
981|				mov64 Ra(tmp_reg), ((uintptr_t)zv)
982|				SSE_AVX_INS movsd, vmovsd, xmm(dst_reg-ZREG_XMM0), qword [Ra(tmp_reg)]
983|			.endif
984||			} else {
985|				SSE_AVX_INS movsd, vmovsd, xmm(dst_reg-ZREG_XMM0), qword [((uint32_t)(uintptr_t)zv)]
986||			}
987|			DOUBLE_SET_ZVAL_DVAL dst_addr, ZREG_XMM0
988|			DOUBLE_SET_ZVAL_DVAL res_addr, ZREG_XMM0
989||		} else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) {
990||			if (Z_MODE(dst_addr) == IS_REG) {
991|				DOUBLE_GET_LONG Z_REG(dst_addr), Z_LVAL_P(zv), ZREG_R0
992|				DOUBLE_SET_ZVAL_DVAL res_addr, Z_REG(dst_addr)
993||			} else if (Z_MODE(res_addr) == IS_REG) {
994|				DOUBLE_GET_LONG Z_REG(res_addr), Z_LVAL_P(zv), ZREG_R0
995|				DOUBLE_SET_ZVAL_DVAL dst_addr, Z_REG(res_addr)
996||			} else {
997|				DOUBLE_GET_LONG ZREG_XMM0, Z_LVAL_P(zv), ZREG_R0
998|				DOUBLE_SET_ZVAL_DVAL dst_addr, ZREG_XMM0
999|				DOUBLE_SET_ZVAL_DVAL res_addr, ZREG_XMM0
1000||			}
1001||		} else if (Z_LVAL_P(zv) == 0 && (Z_MODE(dst_addr) == IS_REG || Z_MODE(res_addr) == IS_REG)) {
1002||				if (Z_MODE(dst_addr) == IS_REG) {
1003|					xor Ra(Z_REG(dst_addr)), Ra(Z_REG(dst_addr))
1004|					SET_ZVAL_LVAL res_addr, Ra(Z_REG(dst_addr))
1005||				} else {
1006|					xor Ra(Z_REG(res_addr)), Ra(Z_REG(res_addr))
1007|					SET_ZVAL_LVAL dst_addr, Ra(Z_REG(res_addr))
1008||				}
1009||		} else {
1010|			.if X64
1011||				if (!IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
1012||					if (Z_MODE(dst_addr) == IS_REG) {
1013|						mov64 Ra(Z_REG(dst_addr)), ((uintptr_t)Z_LVAL_P(zv))
1014|						SET_ZVAL_LVAL res_addr, Ra(Z_REG(dst_addr))
1015||					} else if (Z_MODE(res_addr) == IS_REG) {
1016|						mov64 Ra(Z_REG(res_addr)), ((uintptr_t)Z_LVAL_P(zv))
1017|						SET_ZVAL_LVAL dst_addr, Ra(Z_REG(res_addr))
1018||					} else {
1019|						mov64 Ra(tmp_reg), ((uintptr_t)Z_LVAL_P(zv))
1020|						SET_ZVAL_LVAL dst_addr, Ra(tmp_reg)
1021|						SET_ZVAL_LVAL res_addr, Ra(tmp_reg)
1022||					}
1023||				} else if (Z_MODE(dst_addr) == IS_REG) {
1024|					SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv)
1025|					SET_ZVAL_LVAL res_addr, Ra(Z_REG(dst_addr))
1026||				} else if (Z_MODE(res_addr) == IS_REG) {
1027|					SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv)
1028|					SET_ZVAL_LVAL dst_addr, Ra(Z_REG(res_addr))
1029||				} else {
1030|					SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv)
1031|					SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv)
1032||				}
1033|			.else
1034||				if (Z_MODE(dst_addr) == IS_REG) {
1035|					SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv)
1036|					SET_ZVAL_LVAL res_addr, Ra(Z_REG(dst_addr))
1037||				} else if (Z_MODE(res_addr) == IS_REG) {
1038|					SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv)
1039|					SET_ZVAL_LVAL dst_addr, Ra(Z_REG(res_addr))
1040||				} else {
1041|					SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv)
1042|					SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv)
1043||				}
1044|			.endif
1045||		}
1046||	}
1047||	if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
1048||		if (dst_def_info == MAY_BE_DOUBLE) {
1049||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
1050|				SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE
1051||			}
1052||		} 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) {
1053|			SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv)
1054||		}
1055||	}
1056||	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
1057||		if (dst_def_info == MAY_BE_DOUBLE) {
1058|			SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
1059||		} else {
1060|			SET_ZVAL_TYPE_INFO res_addr, Z_TYPE_INFO_P(zv)
1061||		}
1062||	}
1063|.endmacro
1064
1065/* the same as above, but "src" may overlap with "tmp_reg1" */
1066|.macro ZVAL_COPY_VALUE, dst_addr, dst_info, src_addr, src_info, tmp_reg1, tmp_reg2
1067|	ZVAL_COPY_VALUE_V dst_addr, dst_info, src_addr, src_info, tmp_reg1, tmp_reg2
1068||	if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) &&
1069||      !(src_info & MAY_BE_GUARD) &&
1070||		has_concrete_type(src_info & MAY_BE_ANY)) {
1071||		if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
1072||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD))) {
1073||				uint8_t type = concrete_type(src_info);
1074|				SET_ZVAL_TYPE_INFO dst_addr, type
1075||			}
1076||		}
1077||	} else {
1078|		GET_ZVAL_TYPE_INFO Rd(tmp_reg1), src_addr
1079|		SET_ZVAL_TYPE_INFO dst_addr, Rd(tmp_reg1)
1080||	}
1081|.endmacro
1082
1083|.macro ZVAL_COPY_VALUE_V, dst_addr, dst_info, src_addr, src_info, tmp_reg1, tmp_reg2
1084||	if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
1085||		if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_LONG) {
1086||			if (Z_MODE(src_addr) == IS_REG) {
1087||				if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) {
1088|					SET_ZVAL_LVAL dst_addr, Ra(Z_REG(src_addr))
1089||				}
1090||			} else if (Z_MODE(dst_addr) == IS_REG) {
1091|				GET_ZVAL_LVAL Z_REG(dst_addr), src_addr
1092||			} else {
1093|				GET_ZVAL_LVAL tmp_reg2, src_addr
1094|				SET_ZVAL_LVAL dst_addr, Ra(tmp_reg2)
1095||			}
1096||		} else if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
1097||			if (Z_MODE(src_addr) == IS_REG) {
1098|				DOUBLE_SET_ZVAL_DVAL dst_addr, Z_REG(src_addr)
1099||			} else if (Z_MODE(dst_addr) == IS_REG) {
1100|				DOUBLE_GET_ZVAL_DVAL Z_REG(dst_addr), src_addr
1101||			} else {
1102|				DOUBLE_GET_ZVAL_DVAL ZREG_XMM0, src_addr
1103|				DOUBLE_SET_ZVAL_DVAL dst_addr, ZREG_XMM0
1104||			}
1105||		} else if (!(src_info & (MAY_BE_DOUBLE|MAY_BE_GUARD))) {
1106|			GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1107|			SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1108||		} else {
1109|			.if X64
1110|				GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1111|				SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1112|			.else
1113||				if ((tmp_reg1 == tmp_reg2 || tmp_reg1 == Z_REG(src_addr))) {
1114|					GET_ZVAL_W2 Ra(tmp_reg2), src_addr
1115|					SET_ZVAL_W2 dst_addr, Ra(tmp_reg2)
1116|					GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1117|					SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1118||				} else {
1119|					GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1120|					GET_ZVAL_W2 Ra(tmp_reg1), src_addr
1121|					SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1122|					SET_ZVAL_W2 dst_addr, Ra(tmp_reg1)
1123||				}
1124|			.endif
1125||		}
1126||	}
1127|.endmacro
1128
1129|.macro ZVAL_COPY_VALUE_2, dst_addr, dst_info, res_addr, src_addr, src_info, tmp_reg1, tmp_reg2
1130||	if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
1131||		if ((src_info & MAY_BE_ANY) == MAY_BE_LONG) {
1132||			if (Z_MODE(src_addr) == IS_REG) {
1133||				if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) {
1134|					SET_ZVAL_LVAL dst_addr, Ra(Z_REG(src_addr))
1135||				}
1136||				if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(src_addr)) {
1137|					SET_ZVAL_LVAL res_addr, Ra(Z_REG(src_addr))
1138||				}
1139||			} else if (Z_MODE(dst_addr) == IS_REG) {
1140|				GET_ZVAL_LVAL Z_REG(dst_addr), src_addr
1141||				if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(dst_addr)) {
1142|					SET_ZVAL_LVAL res_addr, Ra(Z_REG(dst_addr))
1143||				}
1144||			} else if (Z_MODE(res_addr) == IS_REG) {
1145|				GET_ZVAL_LVAL Z_REG(res_addr), src_addr
1146|				SET_ZVAL_LVAL dst_addr, Ra(Z_REG(res_addr))
1147||			} else {
1148|				GET_ZVAL_LVAL tmp_reg2, src_addr
1149|				SET_ZVAL_LVAL dst_addr, Ra(tmp_reg2)
1150|				SET_ZVAL_LVAL res_addr, Ra(tmp_reg2)
1151||			}
1152||		} else if ((src_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
1153||			if (Z_MODE(src_addr) == IS_REG) {
1154|				DOUBLE_SET_ZVAL_DVAL dst_addr, Z_REG(src_addr)
1155|				DOUBLE_SET_ZVAL_DVAL res_addr, Z_REG(src_addr)
1156||			} else if (Z_MODE(dst_addr) == IS_REG) {
1157|				DOUBLE_GET_ZVAL_DVAL Z_REG(dst_addr), src_addr
1158|				DOUBLE_SET_ZVAL_DVAL res_addr, Z_REG(dst_addr)
1159||			} else if (Z_MODE(res_addr) == IS_REG) {
1160|				DOUBLE_GET_ZVAL_DVAL Z_REG(res_addr), src_addr
1161|				DOUBLE_SET_ZVAL_DVAL dst_addr, Z_REG(res_addr)
1162||			} else {
1163|				DOUBLE_GET_ZVAL_DVAL ZREG_XMM0, src_addr
1164|				DOUBLE_SET_ZVAL_DVAL dst_addr, ZREG_XMM0
1165|				DOUBLE_SET_ZVAL_DVAL res_addr, ZREG_XMM0
1166||			}
1167||		} else if (!(src_info & MAY_BE_DOUBLE)) {
1168|			GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1169|			SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1170|			SET_ZVAL_PTR res_addr, Ra(tmp_reg2)
1171||		} else {
1172|			.if X64
1173|				GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1174|				SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1175|				SET_ZVAL_PTR res_addr, Ra(tmp_reg2)
1176|			.else
1177||				if (tmp_reg1 == tmp_reg2 || tmp_reg1 == Z_REG(src_addr)) {
1178|					GET_ZVAL_W2 Ra(tmp_reg2), src_addr
1179|					SET_ZVAL_W2 dst_addr, Ra(tmp_reg2)
1180|					SET_ZVAL_W2 res_addr, Ra(tmp_reg2)
1181|					GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1182|					SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1183|					SET_ZVAL_PTR res_addr, Ra(tmp_reg2)
1184||				} else {
1185|					GET_ZVAL_PTR Ra(tmp_reg2), src_addr
1186|					GET_ZVAL_W2 Ra(tmp_reg1), src_addr
1187|					SET_ZVAL_PTR dst_addr, Ra(tmp_reg2)
1188|					SET_ZVAL_PTR res_addr, Ra(tmp_reg2)
1189|					SET_ZVAL_W2 dst_addr, Ra(tmp_reg1)
1190|					SET_ZVAL_W2 res_addr, Ra(tmp_reg1)
1191||				}
1192|			.endif
1193||		}
1194||	}
1195||	if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) &&
1196||	    has_concrete_type(src_info & MAY_BE_ANY)) {
1197||		uint8_t type = concrete_type(src_info);
1198||		if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
1199||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF))) {
1200|				SET_ZVAL_TYPE_INFO dst_addr, type
1201||			}
1202||		}
1203||		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
1204|			SET_ZVAL_TYPE_INFO res_addr, type
1205||		}
1206||	} else {
1207|		GET_ZVAL_TYPE_INFO Rd(tmp_reg1), src_addr
1208|		SET_ZVAL_TYPE_INFO dst_addr, Rd(tmp_reg1)
1209|		SET_ZVAL_TYPE_INFO res_addr, Rd(tmp_reg1)
1210||	}
1211|.endmacro
1212
1213|.macro IF_UNDEF, type_reg, label
1214|	test type_reg, type_reg
1215|	je label
1216|.endmacro
1217
1218|.macro IF_TYPE, type, val, label
1219|	cmp type, val
1220|	je label
1221|.endmacro
1222
1223|.macro IF_NOT_TYPE, type, val, label
1224|	cmp type, val
1225|	jne label
1226|.endmacro
1227
1228|.macro IF_Z_TYPE, zv, val, label
1229|	IF_TYPE byte [zv+offsetof(zval, u1.v.type)], val, label
1230|.endmacro
1231
1232|.macro IF_NOT_Z_TYPE, zv, val, label
1233|	IF_NOT_TYPE byte [zv+offsetof(zval, u1.v.type)], val, label
1234|.endmacro
1235
1236|.macro CMP_ZVAL_TYPE, addr, val
1237||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1238|	cmp byte [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval, u1.v.type)], val
1239|.endmacro
1240
1241|.macro IF_ZVAL_TYPE, addr, val, label
1242||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1243|	IF_TYPE byte [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval, u1.v.type)], val, label
1244|.endmacro
1245
1246|.macro IF_NOT_ZVAL_TYPE, addr, val, label
1247||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1248|	IF_NOT_TYPE byte [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval, u1.v.type)], val, label
1249|.endmacro
1250
1251|.macro IF_FLAGS, type_flags, mask, label
1252|	test type_flags, mask
1253|	jnz label
1254|.endmacro
1255
1256|.macro IF_NOT_FLAGS, type_flags, mask, label
1257|	test type_flags, mask
1258|	jz label
1259|.endmacro
1260
1261|.macro IF_REFCOUNTED, type_flags, label
1262|	IF_FLAGS type_flags, IS_TYPE_REFCOUNTED, label
1263|.endmacro
1264
1265|.macro IF_NOT_REFCOUNTED, type_flags, label
1266|	//IF_NOT_FLAGS type_flags, IS_TYPE_REFCOUNTED, label
1267|	test type_flags, type_flags
1268|	jz label
1269|.endmacro
1270
1271|.macro IF_ZVAL_FLAGS, addr, mask, label
1272||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1273|	IF_FLAGS byte [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval, u1.v.type_flags)], mask, label
1274|.endmacro
1275
1276|.macro IF_NOT_ZVAL_FLAGS, addr, mask, label
1277||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1278|	IF_NOT_FLAGS byte [Ra(Z_REG(addr))+Z_OFFSET(addr)+offsetof(zval, u1.v.type_flags)], mask, label
1279|.endmacro
1280
1281|.macro IF_ZVAL_REFCOUNTED, addr, label
1282|	IF_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label
1283|.endmacro
1284
1285|.macro IF_NOT_ZVAL_REFCOUNTED, addr, label
1286|	IF_NOT_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label
1287|.endmacro
1288
1289|.macro IF_NOT_ZVAL_COLLECTABLE, addr, label
1290|	IF_NOT_ZVAL_FLAGS addr, IS_TYPE_COLLECTABLE, label
1291|.endmacro
1292
1293|.macro GC_ADDREF, zv
1294|	add dword [zv], 1
1295|.endmacro
1296
1297|.macro GC_DELREF, zv
1298|	sub dword [zv], 1
1299|.endmacro
1300
1301|.macro IF_GC_MAY_NOT_LEAK, ptr, label
1302|	test dword [ptr+4],(GC_INFO_MASK | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT))
1303|	jne label
1304|.endmacro
1305
1306|.macro ADDREF_CONST, zv, tmp_reg
1307|	.if X64
1308||		if (!IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
1309|			mov64 tmp_reg, ((uintptr_t)Z_LVAL_P(zv))
1310|			add dword [tmp_reg], 1
1311||		} else {
1312|			add dword [Z_LVAL_P(zv)], 1
1313||		}
1314|	.else
1315|		add dword [Z_LVAL_P(zv)], 1
1316|	.endif
1317|.endmacro
1318
1319|.macro ADDREF_CONST_2, zv, tmp_reg
1320|	.if X64
1321||		if (!IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
1322|			mov64 tmp_reg, ((uintptr_t)Z_LVAL_P(zv))
1323|			add dword [tmp_reg], 2
1324||		} else {
1325|			add dword [Z_LVAL_P(zv)], 2
1326||		}
1327|	.else
1328|		add dword [Z_LVAL_P(zv)], 2
1329|	.endif
1330|.endmacro
1331
1332|.macro TRY_ADDREF, val_info, type_flags_reg, value_ptr_reg
1333||	if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
1334||		if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1335|			IF_NOT_REFCOUNTED type_flags_reg, >1
1336||		}
1337|		GC_ADDREF value_ptr_reg
1338|1:
1339||	}
1340|.endmacro
1341
1342|.macro TRY_ADDREF_2, val_info, type_flags_reg, value_ptr_reg
1343||	if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
1344||		if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1345|			IF_NOT_REFCOUNTED type_flags_reg, >1
1346||		}
1347|		add dword [value_ptr_reg], 2
1348|1:
1349||	}
1350|.endmacro
1351
1352|.macro ZVAL_DEREF, reg, info
1353||	if (info & MAY_BE_REF) {
1354|		IF_NOT_Z_TYPE, reg, IS_REFERENCE, >1
1355|		GET_Z_PTR reg, reg
1356|		add reg, offsetof(zend_reference, val)
1357|1:
1358||	}
1359|.endmacro
1360
1361|.macro SET_EX_OPLINE, op, tmp_reg
1362||	if (op == last_valid_opline) {
1363||		zend_jit_use_last_valid_opline();
1364|		SAVE_IP
1365||	} else {
1366|		ADDR_STORE aword EX->opline, op, tmp_reg
1367||		if (!GCC_GLOBAL_REGS) {
1368||			zend_jit_reset_last_valid_opline();
1369||		}
1370||	}
1371|.endmacro
1372
1373// zval should be in FCARG1a
1374|.macro ZVAL_DTOR_FUNC, var_info, opline // arg1 must be in FCARG1a
1375||	do {
1376||		if (!((var_info) & MAY_BE_GUARD)
1377||		 && has_concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1378||			uint8_t type = concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
1379||			if (type == IS_STRING && !ZEND_DEBUG) {
1380|				EXT_CALL _efree, r0
1381||				break;
1382||			} else if (type == IS_ARRAY) {
1383||				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)) {
1384||					if (opline && ((var_info) & (MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_REF))) {
1385|						SET_EX_OPLINE opline, r0
1386||					}
1387|					EXT_CALL zend_array_destroy, r0
1388||				} else {
1389|					EXT_CALL zend_jit_array_free, r0
1390||				}
1391||				break;
1392||			} else if (type == IS_OBJECT) {
1393||				if (opline) {
1394|					SET_EX_OPLINE opline, r0
1395||				}
1396|				EXT_CALL zend_objects_store_del, r0
1397||				break;
1398||			}
1399||		}
1400||		if (opline) {
1401|			SET_EX_OPLINE opline, r0
1402||		}
1403|		EXT_CALL rc_dtor_func, r0
1404||	} while(0);
1405|.endmacro
1406
1407|.macro ZVAL_PTR_DTOR, addr, op_info, gc, cold, opline
1408||	if ((op_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF|MAY_BE_GUARD)) {
1409||		if ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1410|			// if (Z_REFCOUNTED_P(cv)) {
1411||			if (cold) {
1412|				IF_ZVAL_REFCOUNTED addr, >1
1413|.cold_code
1414|1:
1415||			} else {
1416|				IF_NOT_ZVAL_REFCOUNTED addr, >4
1417||			}
1418||		}
1419|		// if (!Z_DELREF_P(cv)) {
1420|		GET_ZVAL_PTR FCARG1a, addr
1421|		GC_DELREF FCARG1a
1422||		if (((op_info) & MAY_BE_GUARD) || RC_MAY_BE_1(op_info)) {
1423||			if (((op_info) & MAY_BE_GUARD) || RC_MAY_BE_N(op_info)) {
1424||				if (gc && (((op_info) & MAY_BE_GUARD) || (RC_MAY_BE_N(op_info) && ((op_info) & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0))) {
1425|					jnz >3
1426||				} else {
1427|					jnz >4
1428||				}
1429||			}
1430|			// zval_dtor_func(r);
1431|			ZVAL_DTOR_FUNC op_info, opline
1432||			if (gc && (((op_info) & MAY_BE_GUARD) || (RC_MAY_BE_N(op_info) && ((op_info) & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0))) {
1433|				jmp >4
1434||			}
1435|3:
1436||		}
1437||		if (gc && (((op_info) & MAY_BE_GUARD) || (RC_MAY_BE_N(op_info) && ((op_info) & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0))) {
1438||			if ((op_info) & (MAY_BE_REF|MAY_BE_GUARD)) {
1439||				zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, offsetof(zend_reference, val));
1440|				IF_NOT_ZVAL_TYPE addr, IS_REFERENCE, >1
1441|				IF_NOT_ZVAL_COLLECTABLE ref_addr, >4
1442|				GET_ZVAL_PTR FCARG1a, ref_addr
1443|1:
1444||			}
1445|			IF_GC_MAY_NOT_LEAK FCARG1a, >4
1446|			// gc_possible_root(Z_COUNTED_P(z))
1447|			EXT_CALL gc_possible_root, r0
1448||		}
1449||		if (cold && ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) != 0) {
1450|			jmp >4
1451|.code
1452||		}
1453|4:
1454||	}
1455|.endmacro
1456
1457|.macro FREE_OP, op_type, op, op_info, cold, opline
1458||	if (op_type & (IS_VAR|IS_TMP_VAR)) {
1459|		ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var), op_info, 0, cold, opline
1460||	}
1461|.endmacro
1462
1463|.macro SEPARATE_ARRAY, addr, op_info, cold
1464||	if (RC_MAY_BE_N(op_info)) {
1465||		if (Z_REG(addr) != ZREG_FP) {
1466|			GET_ZVAL_LVAL ZREG_R0, addr
1467||			if (RC_MAY_BE_1(op_info)) {
1468|				cmp dword [r0], 1 // if (GC_REFCOUNT() > 1)
1469|				jbe >2
1470||			}
1471||			if (Z_REG(addr) != ZREG_FCARG1 || Z_OFFSET(addr) != 0) {
1472|				LOAD_ZVAL_ADDR FCARG1a, addr
1473||			}
1474|			EXT_CALL zend_jit_zval_array_dup, r0
1475|2:
1476|			mov FCARG1a, r0
1477||		} else {
1478|			GET_ZVAL_LVAL ZREG_FCARG1, addr
1479||			if (RC_MAY_BE_1(op_info)) {
1480|				cmp dword [FCARG1a], 1 // if (GC_REFCOUNT() > 1)
1481||				if (cold) {
1482|					ja >1
1483|.cold_code
1484|1:
1485||				} else {
1486|					jbe >2
1487||				}
1488||			}
1489|			IF_NOT_ZVAL_REFCOUNTED addr, >1
1490|			GC_DELREF FCARG1a
1491|1:
1492|			EXT_CALL zend_array_dup, r0
1493|			SET_ZVAL_PTR addr, r0
1494|			SET_ZVAL_TYPE_INFO addr, IS_ARRAY_EX
1495|			mov FCARG1a, r0
1496||			if (RC_MAY_BE_1(op_info)) {
1497||				if (cold) {
1498|					jmp >2
1499|.code
1500||				}
1501||			}
1502|2:
1503||		}
1504||	} else {
1505|		GET_ZVAL_LVAL ZREG_FCARG1, addr
1506||	}
1507|.endmacro
1508
1509|.macro EFREE_REG_REFERENCE
1510||#if ZEND_DEBUG
1511|		xor FCARG2a, FCARG2a // filename
1512|		.if X64WIN
1513|			xor CARG3d, CARG3d // lineno
1514|			xor CARG4, CARG4
1515|			mov aword A5, 0
1516|			EXT_CALL _efree, r0
1517|		.elif X64
1518|			xor CARG3d, CARG3d // lineno
1519|			xor CARG4, CARG4
1520|			xor CARG5, CARG5
1521|			EXT_CALL _efree, r0
1522|		.else
1523|			sub r4, 4
1524|			push 0
1525|			push 0
1526|			push 0 // lineno
1527|			EXT_CALL _efree, r0
1528|			add r4, 4
1529|		.endif
1530||#else
1531||#ifdef HAVE_BUILTIN_CONSTANT_P
1532|		EXT_CALL _efree_32, r0
1533||#else
1534|		EXT_CALL _efree, r0
1535||#endif
1536||#endif
1537|.endmacro
1538
1539|.macro EFREE_REFERENCE, ptr
1540|	mov FCARG1a, ptr
1541|	EFREE_REG_REFERENCE
1542|.endmacro
1543
1544|.macro EMALLOC, size, op_array, opline
1545||#if ZEND_DEBUG
1546||		const char *filename = op_array->filename ? op_array->filename->val : NULL;
1547|		mov FCARG1a, size
1548|		LOAD_ADDR FCARG2a, filename
1549|		.if X64WIN
1550|			mov CARG3d, opline->lineno
1551|			xor CARG4, CARG4
1552|			mov aword A5, 0
1553|			EXT_CALL _emalloc, r0
1554|		.elif X64
1555|			mov CARG3d, opline->lineno
1556|			xor CARG4, CARG4
1557|			xor CARG5, CARG5
1558|			EXT_CALL _emalloc, r0
1559|		.else
1560|			sub r4, 4
1561|			push 0
1562|			push 0
1563|			push opline->lineno
1564|			EXT_CALL _emalloc, r0
1565|			add r4, 4
1566|		.endif
1567||#else
1568||#ifdef HAVE_BUILTIN_CONSTANT_P
1569||	if (size > 24 && size <= 32) {
1570|		EXT_CALL _emalloc_32, r0
1571||	} else {
1572|		mov FCARG1a, size
1573|		EXT_CALL _emalloc, r0
1574||	}
1575||#else
1576|		mov FCARG1a, size
1577|		EXT_CALL _emalloc, r0
1578||#endif
1579||#endif
1580|.endmacro
1581
1582|.macro OBJ_RELEASE, reg, exit_label
1583|	GC_DELREF Ra(reg)
1584|	jne >1
1585|	// zend_objects_store_del(obj);
1586||	if (reg != ZREG_FCARG1) {
1587|		mov FCARG1a, Ra(reg)
1588||	}
1589|	EXT_CALL zend_objects_store_del, r0
1590|	jmp exit_label
1591|1:
1592|	IF_GC_MAY_NOT_LEAK Ra(reg), >1
1593|	// gc_possible_root(obj)
1594||	if (reg != ZREG_FCARG1) {
1595|		mov FCARG1a, Ra(reg)
1596||	}
1597|	EXT_CALL gc_possible_root, r0
1598|1:
1599|.endmacro
1600
1601|.macro UNDEFINED_OFFSET, opline
1602||	if (opline == last_valid_opline) {
1603||		zend_jit_use_last_valid_opline();
1604|		call ->undefined_offset_ex
1605||	} else {
1606|		SET_EX_OPLINE  opline, r0
1607|		call ->undefined_offset
1608||	}
1609|.endmacro
1610
1611|.macro UNDEFINED_INDEX, opline
1612||	if (opline == last_valid_opline) {
1613||		zend_jit_use_last_valid_opline();
1614|		call ->undefined_index_ex
1615||	} else {
1616|		SET_EX_OPLINE opline, r0
1617|		call ->undefined_index
1618||	}
1619|.endmacro
1620
1621|.macro CANNOT_ADD_ELEMENT, opline
1622||	if (opline == last_valid_opline) {
1623||		zend_jit_use_last_valid_opline();
1624|		call ->cannot_add_element_ex
1625||	} else {
1626|		SET_EX_OPLINE opline, r0
1627|		call ->cannot_add_element
1628||	}
1629|.endmacro
1630
1631|.macro ENDBR
1632||#if defined (__CET__) && (__CET__ & 1) != 0
1633|	.if X64
1634|		endbr64
1635|	.else
1636|		endbr32
1637|	.endif
1638||#endif
1639|.endmacro
1640
1641#if defined (__CET__) && (__CET__ & 1) != 0
1642# define ENDBR_PADDING 4
1643#else
1644# define ENDBR_PADDING 0
1645#endif
1646
1647static bool reuse_ip = 0;
1648static bool delayed_call_chain = 0;
1649static uint32_t  delayed_call_level = 0;
1650static const zend_op *last_valid_opline = NULL;
1651static bool use_last_vald_opline = 0;
1652static bool track_last_valid_opline = 0;
1653static int jit_return_label = -1;
1654static uint32_t current_trace_num = 0;
1655static uint32_t allowed_opt_flags = 0;
1656
1657static void zend_jit_track_last_valid_opline(void)
1658{
1659	use_last_vald_opline = 0;
1660	track_last_valid_opline = 1;
1661}
1662
1663static void zend_jit_use_last_valid_opline(void)
1664{
1665	if (track_last_valid_opline) {
1666		use_last_vald_opline = 1;
1667		track_last_valid_opline = 0;
1668	}
1669}
1670
1671static bool zend_jit_trace_uses_initial_ip(void)
1672{
1673	return use_last_vald_opline;
1674}
1675
1676static void zend_jit_set_last_valid_opline(const zend_op *target_opline)
1677{
1678	if (!reuse_ip) {
1679		track_last_valid_opline = 0;
1680		last_valid_opline = target_opline;
1681	}
1682}
1683
1684static void zend_jit_reset_last_valid_opline(void)
1685{
1686	track_last_valid_opline = 0;
1687	last_valid_opline = NULL;
1688}
1689
1690static void zend_jit_start_reuse_ip(void)
1691{
1692	zend_jit_reset_last_valid_opline();
1693	reuse_ip = 1;
1694}
1695
1696static int zend_jit_reuse_ip(dasm_State **Dst)
1697{
1698	if (!reuse_ip) {
1699		zend_jit_start_reuse_ip();
1700		|	// call = EX(call);
1701		|	mov RX, EX->call
1702	}
1703	return 1;
1704}
1705
1706static void zend_jit_stop_reuse_ip(void)
1707{
1708	reuse_ip = 0;
1709}
1710
1711static int zend_jit_interrupt_handler_stub(dasm_State **Dst)
1712{
1713	|->interrupt_handler:
1714	|	SAVE_IP
1715	|	//EG(vm_interrupt) = 0;
1716	|	MEM_STORE_ZTS byte, executor_globals, vm_interrupt, 0, r0
1717	|	//if (EG(timed_out)) {
1718	|	MEM_CMP_ZTS byte, executor_globals, timed_out, 0, r0
1719	|	je >1
1720	|	//zend_timeout();
1721	|	EXT_CALL zend_timeout, r0
1722	|1:
1723	|	//} else if (zend_interrupt_function) {
1724	if (zend_interrupt_function) {
1725		|	//zend_interrupt_function(execute_data);
1726		|.if X64
1727			|	mov CARG1, FP
1728			|	EXT_CALL zend_interrupt_function, r0
1729		|.else
1730			|	mov aword A1, FP
1731			|	EXT_CALL zend_interrupt_function, r0
1732		|.endif
1733		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
1734		|	je >1
1735		|	EXT_CALL zend_jit_exception_in_interrupt_handler_helper, r0
1736		|1:
1737		|	//ZEND_VM_ENTER();
1738		|	//execute_data = EG(current_execute_data);
1739		|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
1740		|	LOAD_IP
1741	}
1742	|	//ZEND_VM_CONTINUE()
1743	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1744		|	ADD_HYBRID_SPAD
1745		|	JMP_IP
1746	} else if (GCC_GLOBAL_REGS) {
1747		|	add r4, SPAD // stack alignment
1748		|	JMP_IP
1749	} else {
1750		|	mov FP, aword T2 // restore FP
1751		|	mov RX, aword T3 // restore IP
1752		|	add r4, NR_SPAD // stack alignment
1753		|	mov r0, 1 // ZEND_VM_ENTER
1754		|	ret
1755	}
1756
1757	return 1;
1758}
1759
1760static int zend_jit_exception_handler_stub(dasm_State **Dst)
1761{
1762	|->exception_handler:
1763	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1764		const void *handler = zend_get_opcode_handler_func(EG(exception_op));
1765
1766		|	ADD_HYBRID_SPAD
1767		|	EXT_CALL handler, r0
1768		|	JMP_IP
1769	} else {
1770		const void *handler = EG(exception_op)->handler;
1771
1772		if (GCC_GLOBAL_REGS) {
1773			|	add r4, SPAD // stack alignment
1774			|	EXT_JMP handler, r0
1775		} else {
1776			|	mov FCARG1a, FP
1777			|	EXT_CALL handler, r0
1778			|	mov FP, aword T2 // restore FP
1779			|	mov RX, aword T3 // restore IP
1780			|	add r4, NR_SPAD // stack alignment
1781			|	test eax, eax
1782			|	jl >1
1783			|	mov r0, 1 // ZEND_VM_ENTER
1784			|1:
1785			|	ret
1786		}
1787	}
1788
1789	return 1;
1790}
1791
1792static int zend_jit_exception_handler_undef_stub(dasm_State **Dst)
1793{
1794	|->exception_handler_undef:
1795	|	MEM_LOAD_ZTS r0, aword, executor_globals, opline_before_exception, r0
1796	|	test byte OP:r0->result_type, (IS_TMP_VAR|IS_VAR)
1797	|	jz >1
1798	|	mov eax, dword OP:r0->result.var
1799	|	SET_Z_TYPE_INFO FP + r0, IS_UNDEF
1800	|1:
1801	|	jmp ->exception_handler
1802
1803	return 1;
1804}
1805
1806
1807static int zend_jit_exception_handler_free_op1_op2_stub(dasm_State **Dst)
1808{
1809	|->exception_handler_free_op1_op2:
1810	|	UNDEF_OPLINE_RESULT_IF_USED
1811	|	test byte OP:RX->op1_type, (IS_TMP_VAR|IS_VAR)
1812	|	je >9
1813	|	mov eax, dword OP:RX->op1.var
1814	|	add r0, FP
1815	|	ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL
1816	|9:
1817	|	test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR)
1818	|	je >9
1819	|	mov eax, dword OP:RX->op2.var
1820	|	add r0, FP
1821	|	ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL
1822	|9:
1823	|	jmp ->exception_handler
1824	return 1;
1825}
1826
1827static int zend_jit_exception_handler_free_op2_stub(dasm_State **Dst)
1828{
1829	|->exception_handler_free_op2:
1830	|	MEM_LOAD_ZTS RX, aword, executor_globals, opline_before_exception, r0
1831	|	UNDEF_OPLINE_RESULT_IF_USED
1832	|	test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR)
1833	|	je >9
1834	|	mov eax, dword OP:RX->op2.var
1835	|	add r0, FP
1836	|	ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL
1837	|9:
1838	|	jmp ->exception_handler
1839	return 1;
1840}
1841
1842static int zend_jit_leave_function_stub(dasm_State **Dst)
1843{
1844	|->leave_function_handler:
1845	|	mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)]
1846	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1847		|	test FCARG1d, ZEND_CALL_TOP
1848		|	jnz >1
1849		|	EXT_CALL zend_jit_leave_nested_func_helper, r0
1850		|	ADD_HYBRID_SPAD
1851		|	JMP_IP
1852		|1:
1853		|	EXT_CALL zend_jit_leave_top_func_helper, r0
1854		|	ADD_HYBRID_SPAD
1855		|	JMP_IP
1856	} else {
1857		if (GCC_GLOBAL_REGS) {
1858			|	add r4, SPAD
1859		} else {
1860			|	mov FCARG2a, FP
1861			|	mov FP, aword T2 // restore FP
1862			|	mov RX, aword T3 // restore IP
1863			|	add r4, NR_SPAD
1864		}
1865		|	test FCARG1d, ZEND_CALL_TOP
1866		|	jnz >1
1867		|	EXT_JMP zend_jit_leave_nested_func_helper, r0
1868		|1:
1869		|	EXT_JMP zend_jit_leave_top_func_helper, r0
1870	}
1871
1872	return 1;
1873}
1874
1875static int zend_jit_leave_throw_stub(dasm_State **Dst)
1876{
1877	|->leave_throw_handler:
1878	|	// if (opline->opcode != ZEND_HANDLE_EXCEPTION) {
1879	if (GCC_GLOBAL_REGS) {
1880		|	cmp byte OP:IP->opcode, ZEND_HANDLE_EXCEPTION
1881		|	je >5
1882		|	// EG(opline_before_exception) = opline;
1883		|	MEM_STORE_ZTS aword, executor_globals, opline_before_exception, IP, r0
1884		|5:
1885		|	// opline = EG(exception_op);
1886		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op
1887		|	mov aword EX->opline, IP
1888		|	// HANDLE_EXCEPTION()
1889		|	jmp ->exception_handler
1890	} else {
1891		|	GET_IP FCARG1a
1892		|	cmp byte OP:FCARG1a->opcode, ZEND_HANDLE_EXCEPTION
1893		|	je >5
1894		|	// EG(opline_before_exception) = opline;
1895		|	MEM_STORE_ZTS aword, executor_globals, opline_before_exception, FCARG1a, r0
1896		|5:
1897		|	// opline = EG(exception_op);
1898		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op
1899		|	mov FP, aword T2 // restore FP
1900		|	mov RX, aword T3 // restore IP
1901		|	add r4, NR_SPAD // stack alignment
1902		|	mov r0, 2 // ZEND_VM_LEAVE
1903		|	ret
1904	}
1905
1906	return 1;
1907}
1908
1909static int zend_jit_icall_throw_stub(dasm_State **Dst)
1910{
1911	|->icall_throw_handler:
1912	|	// zend_rethrow_exception(zend_execute_data *execute_data)
1913	|	mov IP, aword EX->opline
1914	|	// if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) {
1915	|	cmp byte OP:IP->opcode, ZEND_HANDLE_EXCEPTION
1916	|	je >1
1917	|	// EG(opline_before_exception) = opline;
1918	|	MEM_STORE_ZTS aword, executor_globals, opline_before_exception, IP, r0
1919	|1:
1920	|	// opline = EG(exception_op);
1921	|	LOAD_IP_ADDR_ZTS executor_globals, exception_op
1922	||	if (GCC_GLOBAL_REGS) {
1923	|		mov aword EX->opline, IP
1924	||	}
1925	|	// HANDLE_EXCEPTION()
1926	|	jmp ->exception_handler
1927
1928	return 1;
1929}
1930
1931static int zend_jit_throw_cannot_pass_by_ref_stub(dasm_State **Dst)
1932{
1933	|->throw_cannot_pass_by_ref:
1934	|	mov r0, EX->opline
1935	|	mov ecx, dword OP:r0->result.var
1936	|	SET_Z_TYPE_INFO RX+r1, IS_UNDEF
1937	|	// last EX(call) frame may be delayed
1938	|	cmp RX, EX->call
1939	|	je >1
1940	|	mov r1, EX->call
1941	|	mov EX:RX->prev_execute_data, r1
1942	|	mov EX->call, RX
1943	|1:
1944	|	mov RX, r0
1945	|	mov FCARG1d, dword OP:r0->op2.num
1946	|	EXT_CALL zend_cannot_pass_by_reference, r0
1947	|	cmp byte OP:RX->op1_type, IS_TMP_VAR
1948	|	jne >9
1949	|	mov eax, dword OP:RX->op1.var
1950	|	add r0, FP
1951	|	ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL
1952	|9:
1953	|	jmp ->exception_handler
1954
1955	return 1;
1956}
1957
1958static int zend_jit_undefined_offset_ex_stub(dasm_State **Dst)
1959{
1960	|->undefined_offset_ex:
1961	|	SAVE_IP
1962	|	jmp ->undefined_offset
1963
1964	return 1;
1965}
1966
1967static int zend_jit_undefined_offset_stub(dasm_State **Dst)
1968{
1969	|->undefined_offset:
1970	||	if (!GCC_GLOBAL_REGS) {
1971	|		mov FCARG1a, FP
1972	||	}
1973	|	EXT_JMP zend_jit_undefined_long_key, r0
1974
1975	return 1;
1976}
1977
1978static int zend_jit_undefined_index_ex_stub(dasm_State **Dst)
1979{
1980	|->undefined_index_ex:
1981	|	SAVE_IP
1982	|	jmp ->undefined_index
1983
1984	return 1;
1985}
1986
1987static int zend_jit_undefined_index_stub(dasm_State **Dst)
1988{
1989	|->undefined_index:
1990	||	if (!GCC_GLOBAL_REGS) {
1991	|		mov FCARG1a, FP
1992	||	}
1993	|	EXT_JMP zend_jit_undefined_string_key, r0
1994
1995	return 1;
1996}
1997
1998static int zend_jit_cannot_add_element_ex_stub(dasm_State **Dst)
1999{
2000	|->cannot_add_element_ex:
2001	|	SAVE_IP
2002	|	jmp ->cannot_add_element
2003
2004	return 1;
2005}
2006
2007static int zend_jit_cannot_add_element_stub(dasm_State **Dst)
2008{
2009	|->cannot_add_element:
2010	|.if X64WIN
2011		|	sub r4, 0x28
2012	|.elif X64
2013		|	sub r4, 8
2014	|.else
2015		|	sub r4, 12
2016	|.endif
2017	|	mov r0, EX->opline
2018	|	cmp byte OP:r0->result_type, IS_UNUSED
2019	|	jz >1
2020	|	mov eax, dword OP:r0->result.var
2021	|	SET_Z_TYPE_INFO FP + r0, IS_NULL
2022	|1:
2023	|.if X64WIN
2024		|	xor CARG1, CARG1
2025		|	LOAD_ADDR CARG2, "Cannot add element to the array as the next element is already occupied"
2026		|	EXT_CALL zend_throw_error, r0
2027		|	add r4, 0x28
2028	|.elif X64
2029		|	xor CARG1, CARG1
2030		|	LOAD_ADDR CARG2, "Cannot add element to the array as the next element is already occupied"
2031		|	EXT_CALL zend_throw_error, r0
2032		|	add r4, 8
2033	|.else
2034		|	sub r4, 8
2035		|	push "Cannot add element to the array as the next element is already occupied"
2036		|	push 0
2037		|	EXT_CALL zend_throw_error, r0
2038		|	add r4, 28
2039	|.endif
2040	|	ret
2041
2042	return 1;
2043}
2044
2045static int zend_jit_undefined_function_stub(dasm_State **Dst)
2046{
2047	|->undefined_function:
2048	|	mov r0, aword EX->opline
2049	|.if X64
2050		|	xor CARG1, CARG1
2051		|	LOAD_ADDR CARG2, "Call to undefined function %s()"
2052		|	movsxd CARG3, dword [r0 + offsetof(zend_op, op2.constant)]
2053		|	mov CARG3, aword [r0 + CARG3]
2054		|	add CARG3, offsetof(zend_string, val)
2055		|	EXT_CALL zend_throw_error, r0
2056	|.else
2057		|	mov r0, aword [r0 + offsetof(zend_op, op2.zv)]
2058		|	mov r0, aword [r0]
2059		|	add r0, offsetof(zend_string, val)
2060		|	mov aword A3, r0
2061		|	mov aword A2, "Call to undefined function %s()"
2062		|	mov aword A1, 0
2063		|	EXT_CALL zend_throw_error, r0
2064	|.endif
2065	|	jmp ->exception_handler
2066	return 1;
2067}
2068
2069static int zend_jit_negative_shift_stub(dasm_State **Dst)
2070{
2071	|->negative_shift:
2072	|	mov RX, EX->opline
2073	|.if X64
2074		|.if WIN
2075		|	LOAD_ADDR CARG1, &zend_ce_arithmetic_error
2076		|	mov CARG1, aword [CARG1]
2077		|.else
2078		|	LOAD_ADDR CARG1, zend_ce_arithmetic_error
2079		|.endif
2080		|	LOAD_ADDR CARG2, "Bit shift by negative number"
2081		|	EXT_CALL zend_throw_error, r0
2082	|.else
2083		|	sub r4, 8
2084		|	push "Bit shift by negative number"
2085		|.if WIN
2086		|	LOAD_ADDR r0, &zend_ce_arithmetic_error
2087		|	push aword [r0]
2088		|.else
2089		|	PUSH_ADDR zend_ce_arithmetic_error, r0
2090		|.endif
2091		|	EXT_CALL zend_throw_error, r0
2092		|	add r4, 16
2093	|.endif
2094	|	jmp ->exception_handler_free_op1_op2
2095	return 1;
2096}
2097
2098static int zend_jit_mod_by_zero_stub(dasm_State **Dst)
2099{
2100	|->mod_by_zero:
2101	|	mov RX, EX->opline
2102	|.if X64
2103		|.if WIN
2104		|	LOAD_ADDR CARG1, &zend_ce_division_by_zero_error
2105		|	mov CARG1, aword [CARG1]
2106		|.else
2107		|	LOAD_ADDR CARG1, zend_ce_division_by_zero_error
2108		|.endif
2109		|	LOAD_ADDR CARG2, "Modulo by zero"
2110		|	EXT_CALL zend_throw_error, r0
2111	|.else
2112		|	sub r4, 8
2113		|	push "Modulo by zero"
2114		|.if WIN
2115		|	LOAD_ADDR r0, &zend_ce_division_by_zero_error
2116		|	push aword [r0]
2117		|.else
2118		|	PUSH_ADDR zend_ce_division_by_zero_error, r0
2119		|.endif
2120		|	EXT_CALL zend_throw_error, r0
2121		|	add r4, 16
2122	|.endif
2123	|	jmp ->exception_handler_free_op1_op2
2124	return 1;
2125}
2126
2127static int zend_jit_invalid_this_stub(dasm_State **Dst)
2128{
2129	|->invalid_this:
2130	|	UNDEF_OPLINE_RESULT
2131	|.if X64
2132		|	xor CARG1, CARG1
2133		|	LOAD_ADDR CARG2, "Using $this when not in object context"
2134		|	EXT_CALL zend_throw_error, r0
2135	|.else
2136		|	sub r4, 8
2137		|	push "Using $this when not in object context"
2138		|	push 0
2139		|	EXT_CALL zend_throw_error, r0
2140		|	add r4, 16
2141	|.endif
2142	|	jmp ->exception_handler
2143	return 1;
2144}
2145
2146static int zend_jit_double_one_stub(dasm_State **Dst)
2147{
2148	|->one:
2149	|.dword 0, 0x3ff00000
2150	return 1;
2151}
2152
2153static int zend_jit_hybrid_runtime_jit_stub(dasm_State **Dst)
2154{
2155	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2156		return 1;
2157	}
2158
2159	|->hybrid_runtime_jit:
2160	|	EXT_CALL zend_runtime_jit, r0
2161	|	JMP_IP
2162	return 1;
2163}
2164
2165static int zend_jit_hybrid_profile_jit_stub(dasm_State **Dst)
2166{
2167	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2168		return 1;
2169	}
2170
2171	|->hybrid_profile_jit:
2172	|	// ++zend_jit_profile_counter;
2173	|	.if X64
2174	|		LOAD_ADDR r0, &zend_jit_profile_counter
2175	|		inc aword [r0]
2176	|	.else
2177	|		inc aword [&zend_jit_profile_counter]
2178	|	.endif
2179	|	// op_array = (zend_op_array*)EX(func);
2180	|	mov r0, EX->func
2181	|	// run_time_cache = EX(run_time_cache);
2182	|	mov r2, EX->run_time_cache
2183	|	// jit_extension = (const void*)ZEND_FUNC_INFO(op_array);
2184	|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2185	|	// ++ZEND_COUNTER_INFO(op_array)
2186	|	inc aword [r2 + zend_jit_profile_counter_rid * sizeof(void*)]
2187	|	// return ((zend_vm_opcode_handler_t)jit_extension->orig_handler)()
2188	|	jmp aword [r0 + offsetof(zend_jit_op_array_extension, orig_handler)]
2189	return 1;
2190}
2191
2192static int zend_jit_hybrid_hot_code_stub(dasm_State **Dst)
2193{
2194	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2195		return 1;
2196	}
2197
2198	|->hybrid_hot_code:
2199	|	mov word [r2], ZEND_JIT_COUNTER_INIT
2200	|	mov FCARG1a, FP
2201	|	GET_IP FCARG2a
2202	|	EXT_CALL zend_jit_hot_func, r0
2203	|	JMP_IP
2204	return 1;
2205}
2206
2207/*
2208 * This code is based Mike Pall's "Hashed profile counters" idea, implemented
2209 * in LuaJIT. The full description may be found in "LuaJIT 2.0 intellectual
2210 * property disclosure and research opportunities" email
2211 * at http://lua-users.org/lists/lua-l/2009-11/msg00089.html
2212 *
2213 * In addition we use a variation of Knuth's multiplicative hash function
2214 * described at https://code.i-harness.com/en/q/a21ce
2215 *
2216 * uint64_t hash(uint64_t x) {
2217 *    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
2218 *    x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
2219 *    x = x ^ (x >> 31);
2220 *    return x;
2221 * }
2222 *
2223 * uint_32_t hash(uint32_t x) {
2224 *    x = ((x >> 16) ^ x) * 0x45d9f3b;
2225 *    x = ((x >> 16) ^ x) * 0x45d9f3b;
2226 *    x = (x >> 16) ^ x;
2227 *    return x;
2228 * }
2229 *
2230 */
2231static int zend_jit_hybrid_hot_counter_stub(dasm_State **Dst, uint32_t cost)
2232{
2233	|	ENDBR
2234	|	mov r0, EX->func
2235	|	mov r1, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2236	|	mov r2, aword [r1 + offsetof(zend_jit_op_array_hot_extension, counter)]
2237	|	sub word [r2], cost
2238	|	jle ->hybrid_hot_code
2239	|	GET_IP r2
2240	|	sub r2, aword [r0 + offsetof(zend_op_array, opcodes)]
2241	|	// divide by sizeof(zend_op)
2242	|	.if X64
2243	||		ZEND_ASSERT(sizeof(zend_op) == 32);
2244	|		sar r2, 2
2245	|	.else
2246	||		ZEND_ASSERT(sizeof(zend_op) == 28);
2247	|		imul r2, 0xb6db6db7
2248	|	.endif
2249	|	.if X64
2250	|		jmp aword [r1+r2+offsetof(zend_jit_op_array_hot_extension, orig_handlers)]
2251	|	.else
2252	|		jmp aword [r1+r2+offsetof(zend_jit_op_array_hot_extension, orig_handlers)]
2253	|	.endif
2254	return 1;
2255}
2256
2257static int zend_jit_hybrid_func_hot_counter_stub(dasm_State **Dst)
2258{
2259	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
2260		return 1;
2261	}
2262
2263	|->hybrid_func_hot_counter:
2264
2265	return zend_jit_hybrid_hot_counter_stub(Dst,
2266		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func)));
2267}
2268
2269static int zend_jit_hybrid_loop_hot_counter_stub(dasm_State **Dst)
2270{
2271	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
2272		return 1;
2273	}
2274
2275	|->hybrid_loop_hot_counter:
2276
2277	return zend_jit_hybrid_hot_counter_stub(Dst,
2278		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
2279}
2280
2281static int zend_jit_hybrid_hot_trace_stub(dasm_State **Dst)
2282{
2283	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2284		return 1;
2285	}
2286
2287	|->hybrid_hot_trace:
2288	|	mov word [r2], ZEND_JIT_COUNTER_INIT
2289	|	mov FCARG1a, FP
2290	|	GET_IP FCARG2a
2291	|	EXT_CALL zend_jit_trace_hot_root, r0
2292	|	test eax, eax // TODO : remove this check at least for HYBRID VM ???
2293	|	jl >1
2294	|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
2295	|	LOAD_IP
2296	|	JMP_IP
2297	|1:
2298	|	EXT_JMP zend_jit_halt_op->handler, r0
2299	return 1;
2300}
2301
2302static int zend_jit_hybrid_trace_counter_stub(dasm_State **Dst, uint32_t cost)
2303{
2304	|	ENDBR
2305	|	mov r0, EX->func
2306	|	mov r1, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2307	|	mov r1, aword [r1 + offsetof(zend_jit_op_array_trace_extension, offset)]
2308	|	mov r2, aword [IP + r1 + offsetof(zend_op_trace_info, counter)]
2309	|	sub word [r2], cost
2310	|	jle ->hybrid_hot_trace
2311	|	jmp aword [IP + r1]
2312	return 1;
2313}
2314
2315static int zend_jit_hybrid_func_trace_counter_stub(dasm_State **Dst)
2316{
2317	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
2318		return 1;
2319	}
2320
2321	|->hybrid_func_trace_counter:
2322
2323	return zend_jit_hybrid_trace_counter_stub(Dst,
2324		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1)  / JIT_G(hot_func)));
2325}
2326
2327static int zend_jit_hybrid_ret_trace_counter_stub(dasm_State **Dst)
2328{
2329	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_return)) {
2330		return 1;
2331	}
2332
2333	|->hybrid_ret_trace_counter:
2334
2335	return zend_jit_hybrid_trace_counter_stub(Dst,
2336		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_return) - 1) / JIT_G(hot_return)));
2337}
2338
2339static int zend_jit_hybrid_loop_trace_counter_stub(dasm_State **Dst)
2340{
2341	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
2342		return 1;
2343	}
2344
2345	|->hybrid_loop_trace_counter:
2346
2347	return zend_jit_hybrid_trace_counter_stub(Dst,
2348		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
2349}
2350
2351static int zend_jit_trace_halt_stub(dasm_State **Dst)
2352{
2353	|->trace_halt:
2354	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2355		|	ADD_HYBRID_SPAD
2356		|	EXT_JMP zend_jit_halt_op->handler, r0
2357	} else if (GCC_GLOBAL_REGS) {
2358		|	add r4, SPAD // stack alignment
2359		|	xor IP, IP // PC must be zero
2360		|	ret
2361	} else {
2362		|	mov FP, aword T2 // restore FP
2363		|	mov RX, aword T3 // restore IP
2364		|	add r4, NR_SPAD // stack alignment
2365		|	mov r0, -1 // ZEND_VM_RETURN
2366		|	ret
2367	}
2368	return 1;
2369}
2370
2371static int zend_jit_trace_exit_stub(dasm_State **Dst)
2372{
2373	|->trace_exit:
2374	|
2375	|	// Save CPU registers
2376	|.if X64
2377	|	sub r4, 16*8+16*8-8 /* CPU regs + SSE regs */
2378	|	mov aword [r4+15*8], r15
2379	|	mov aword [r4+11*8], r11
2380	|	mov aword [r4+10*8], r10
2381	|	mov aword [r4+9*8], r9
2382	|	mov aword [r4+8*8], r8
2383	|	mov aword [r4+7*8], rdi
2384	|	mov aword [r4+6*8], rsi
2385	|	mov aword [r4+2*8], rdx
2386	|	mov aword [r4+1*8], rcx
2387	|	mov aword [r4+0*8], rax
2388	|	mov FCARG1a, aword [r4+16*8+16*8-8] // exit_num = POP
2389	|	mov FCARG2a, r4
2390	|	movsd qword [r4+16*8+15*8], xmm15
2391	|	movsd qword [r4+16*8+14*8], xmm14
2392	|	movsd qword [r4+16*8+13*8], xmm13
2393	|	movsd qword [r4+16*8+12*8], xmm12
2394	|	movsd qword [r4+16*8+11*8], xmm11
2395	|	movsd qword [r4+16*8+10*8], xmm10
2396	|	movsd qword [r4+16*8+9*8], xmm9
2397	|	movsd qword [r4+16*8+8*8], xmm8
2398	|	movsd qword [r4+16*8+7*8], xmm7
2399	|	movsd qword [r4+16*8+6*8], xmm6
2400	|	movsd qword [r4+16*8+5*8], xmm5
2401	|	movsd qword [r4+16*8+4*8], xmm4
2402	|	movsd qword [r4+16*8+3*8], xmm3
2403	|	movsd qword [r4+16*8+2*8], xmm2
2404	|	movsd qword [r4+16*8+1*8], xmm1
2405	|	movsd qword [r4+16*8+0*8], xmm0
2406	|.if X64WIN
2407	|	sub r4, 32 /* shadow space */
2408	|.endif
2409	|.else
2410	|	sub r4, 8*4+8*8-4 /* CPU regs + SSE regs */
2411	|	mov aword [r4+7*4], edi
2412	|	mov aword [r4+2*4], edx
2413	|	mov aword [r4+1*4], ecx
2414	|	mov aword [r4+0*4], eax
2415	|	mov FCARG1a, aword [r4+8*4+8*8-4] // exit_num = POP
2416	|	mov FCARG2a, r4
2417	|	movsd qword [r4+8*4+7*8], xmm7
2418	|	movsd qword [r4+8*4+6*8], xmm6
2419	|	movsd qword [r4+8*4+5*8], xmm5
2420	|	movsd qword [r4+8*4+4*8], xmm4
2421	|	movsd qword [r4+8*4+3*8], xmm3
2422	|	movsd qword [r4+8*4+2*8], xmm2
2423	|	movsd qword [r4+8*4+1*8], xmm1
2424	|	movsd qword [r4+8*4+0*8], xmm0
2425	|.endif
2426	|
2427	|	// EX(opline) = opline
2428	|	SAVE_IP
2429	|	// zend_jit_trace_exit(trace_num, exit_num)
2430	|	EXT_CALL zend_jit_trace_exit, r0
2431	|.if X64WIN
2432	|	add r4, 16*8+16*8+32 /* CPU regs + SSE regs + shadow space */
2433	|.elif X64
2434	|	add r4, 16*8+16*8 /* CPU regs + SSE regs */
2435	|.else
2436	|	add r4, 8*4+8*8 /* CPU regs + SSE regs */
2437	|.endif
2438
2439	|	test eax, eax
2440	|	jne >1
2441
2442	|	// execute_data = EG(current_execute_data)
2443	|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
2444	|	// opline = EX(opline)
2445	|	LOAD_IP
2446
2447	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2448		|	ADD_HYBRID_SPAD
2449		|	JMP_IP
2450	} else if (GCC_GLOBAL_REGS) {
2451		|	add r4, SPAD // stack alignment
2452		|	JMP_IP
2453	} else {
2454		|	mov FP, aword T2 // restore FP
2455		|	mov RX, aword T3 // restore IP
2456		|	add r4, NR_SPAD // stack alignment
2457		|	mov r0, 1 // ZEND_VM_ENTER
2458		|	ret
2459	}
2460
2461	|1:
2462	|	jl ->trace_halt
2463
2464	|	// execute_data = EG(current_execute_data)
2465	|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
2466	|	// opline = EX(opline)
2467	|	LOAD_IP
2468
2469	|	// check for interrupt (try to avoid this ???)
2470	|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
2471	|	jne ->interrupt_handler
2472
2473	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2474		|	ADD_HYBRID_SPAD
2475		|	mov r0, EX->func
2476		|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2477		|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
2478		|	jmp aword [IP + r0]
2479	} else if (GCC_GLOBAL_REGS) {
2480		|	add r4, SPAD // stack alignment
2481		|	mov r0, EX->func
2482		|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2483		|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
2484		|	jmp aword [IP + r0]
2485	} else {
2486		|	mov IP, aword EX->opline
2487		|	mov FCARG1a, FP
2488		|	mov r0, EX->func
2489		|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2490		|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
2491		|	call aword [IP + r0]
2492		|	test eax, eax
2493		|	jl ->trace_halt
2494		|	mov FP, aword T2 // restore FP
2495		|	mov RX, aword T3 // restore IP
2496		|	add r4, NR_SPAD // stack alignment
2497		|	mov r0, 1 // ZEND_VM_ENTER
2498		|	ret
2499	}
2500
2501	return 1;
2502}
2503
2504static int zend_jit_trace_escape_stub(dasm_State **Dst)
2505{
2506	|->trace_escape:
2507	|
2508	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2509		|	ADD_HYBRID_SPAD
2510		|	JMP_IP
2511	} else if (GCC_GLOBAL_REGS) {
2512		|	add r4, SPAD // stack alignment
2513		|	JMP_IP
2514	} else {
2515		|	mov FP, aword T2 // restore FP
2516		|	mov RX, aword T3 // restore IP
2517		|	add r4, NR_SPAD // stack alignment
2518		|	mov r0, 1 // ZEND_VM_ENTER
2519		|	ret
2520	}
2521
2522	return 1;
2523}
2524
2525/* Keep 32 exit points in a single code block */
2526#define ZEND_JIT_EXIT_POINTS_SPACING   4  // push byte + short jmp = bytes
2527#define ZEND_JIT_EXIT_POINTS_PER_GROUP 32 // number of continuous exit points
2528
2529static int zend_jit_trace_exit_group_stub(dasm_State **Dst, uint32_t n)
2530{
2531	uint32_t i;
2532
2533	for (i = 0; i < ZEND_JIT_EXIT_POINTS_PER_GROUP - 1; i++) {
2534		|	push byte i
2535		|	.byte 0xeb, (4*(ZEND_JIT_EXIT_POINTS_PER_GROUP-i)-6) // jmp >1
2536	}
2537	|	push byte i
2538	|// 1:
2539	|	add aword [r4], n
2540	|	jmp ->trace_exit
2541
2542	return 1;
2543}
2544
2545#ifdef CONTEXT_THREADED_JIT
2546static int zend_jit_context_threaded_call_stub(dasm_State **Dst)
2547{
2548	|->context_threaded_call:
2549	|	pop r0
2550	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2551		|	ADD_HYBRID_SPAD
2552		|	jmp aword [IP]
2553	} else if (GCC_GLOBAL_REGS) {
2554		|	add r4, SPAD // stack alignment
2555		|	jmp aword [IP]
2556	} else {
2557		ZEND_UNREACHABLE();
2558		// TODO: context threading can't work without GLOBAL REGS because we have to change
2559		//       the value of execute_data in execute_ex()
2560		|	mov FCARG1a, FP
2561		|	mov r0, aword [FP]
2562		|	mov FP, aword T2 // restore FP
2563		|	mov RX, aword T3 // restore IP
2564		|	add r4, NR_SPAD // stack alignment
2565		|	jmp aword [r0]
2566	}
2567	return 1;
2568}
2569#endif
2570
2571static int zend_jit_assign_const_stub(dasm_State **Dst)
2572{
2573	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2574	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2575	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
2576
2577	|->assign_const:
2578	|.if X64WIN
2579	|	sub r4, 0x28
2580	|.elif X64
2581	|	sub r4, 8
2582	|.else
2583	|	sub r4, 12
2584	|.endif
2585	if (!zend_jit_assign_to_variable(
2586			Dst, NULL,
2587			var_addr, var_addr, -1, -1,
2588			IS_CONST, val_addr, val_info,
2589			0, 0)) {
2590		return 0;
2591	}
2592	|.if X64WIN
2593	|	add r4, 0x28
2594	|.elif X64
2595	|	add r4, 8
2596	|.else
2597	|	add r4, 12
2598	|.endif
2599	|	ret
2600	return 1;
2601}
2602
2603static int zend_jit_assign_tmp_stub(dasm_State **Dst)
2604{
2605	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2606	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2607	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
2608
2609	|->assign_tmp:
2610	|.if X64WIN
2611	|	sub r4, 0x28
2612	|.elif X64
2613	|	sub r4, 8
2614	|.else
2615	|	sub r4, 12
2616	|.endif
2617	if (!zend_jit_assign_to_variable(
2618			Dst, NULL,
2619			var_addr, var_addr, -1, -1,
2620			IS_TMP_VAR, val_addr, val_info,
2621			0, 0)) {
2622		return 0;
2623	}
2624	|.if X64WIN
2625	|	add r4, 0x28
2626	|.elif X64
2627	|	add r4, 8
2628	|.else
2629	|	add r4, 12
2630	|.endif
2631	|	ret
2632	return 1;
2633}
2634
2635static int zend_jit_assign_var_stub(dasm_State **Dst)
2636{
2637	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2638	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2639	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF;
2640
2641	|->assign_var:
2642	|.if X64WIN
2643	|	sub r4, 0x28
2644	|.elif X64
2645	|	sub r4, 8
2646	|.else
2647	|	sub r4, 12
2648	|.endif
2649	if (!zend_jit_assign_to_variable(
2650			Dst, NULL,
2651			var_addr, var_addr, -1, -1,
2652			IS_VAR, val_addr, val_info,
2653			0, 0)) {
2654		return 0;
2655	}
2656	|.if X64WIN
2657	|	add r4, 0x28
2658	|.elif X64
2659	|	add r4, 8
2660	|.else
2661	|	add r4, 12
2662	|.endif
2663	|	ret
2664	return 1;
2665}
2666
2667static int zend_jit_assign_cv_noref_stub(dasm_State **Dst)
2668{
2669	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2670	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2671	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN/*|MAY_BE_UNDEF*/;
2672
2673	|->assign_cv_noref:
2674	|.if X64WIN
2675	|	sub r4, 0x28
2676	|.elif X64
2677	|	sub r4, 8
2678	|.else
2679	|	sub r4, 12
2680	|.endif
2681	if (!zend_jit_assign_to_variable(
2682			Dst, NULL,
2683			var_addr, var_addr, -1, -1,
2684			IS_CV, val_addr, val_info,
2685			0, 0)) {
2686		return 0;
2687	}
2688	|.if X64WIN
2689	|	add r4, 0x28
2690	|.elif X64
2691	|	add r4, 8
2692	|.else
2693	|	add r4, 12
2694	|.endif
2695	|	ret
2696	return 1;
2697}
2698
2699static int zend_jit_assign_cv_stub(dasm_State **Dst)
2700{
2701	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2702	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2703	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF/*|MAY_BE_UNDEF*/;
2704
2705	|->assign_cv:
2706	|.if X64WIN
2707	|	sub r4, 0x28
2708	|.elif X64
2709	|	sub r4, 8
2710	|.else
2711	|	sub r4, 12
2712	|.endif
2713	if (!zend_jit_assign_to_variable(
2714			Dst, NULL,
2715			var_addr, var_addr, -1, -1,
2716			IS_CV, val_addr, val_info,
2717			0, 0)) {
2718		return 0;
2719	}
2720	|.if X64WIN
2721	|	add r4, 0x28
2722	|.elif X64
2723	|	add r4, 8
2724	|.else
2725	|	add r4, 12
2726	|.endif
2727	|	ret
2728	return 1;
2729}
2730
2731static const zend_jit_stub zend_jit_stubs[] = {
2732	JIT_STUB(interrupt_handler,         SP_ADJ_JIT,  SP_ADJ_VM),
2733	JIT_STUB(exception_handler,         SP_ADJ_JIT,  SP_ADJ_VM),
2734	JIT_STUB(exception_handler_undef,   SP_ADJ_JIT,  SP_ADJ_VM),
2735	JIT_STUB(exception_handler_free_op1_op2, SP_ADJ_JIT,  SP_ADJ_VM),
2736	JIT_STUB(exception_handler_free_op2,     SP_ADJ_JIT,  SP_ADJ_VM),
2737	JIT_STUB(leave_function,            SP_ADJ_JIT,  SP_ADJ_VM),
2738	JIT_STUB(leave_throw,               SP_ADJ_JIT,  SP_ADJ_VM),
2739	JIT_STUB(icall_throw,               SP_ADJ_JIT,  SP_ADJ_VM),
2740	JIT_STUB(throw_cannot_pass_by_ref,  SP_ADJ_JIT,  SP_ADJ_VM),
2741	JIT_STUB(undefined_offset,          SP_ADJ_JIT,  SP_ADJ_VM),
2742	JIT_STUB(undefined_index,           SP_ADJ_JIT,  SP_ADJ_VM),
2743	JIT_STUB(cannot_add_element,        SP_ADJ_JIT,  SP_ADJ_VM),
2744	JIT_STUB(undefined_offset_ex,       SP_ADJ_JIT,  SP_ADJ_VM),
2745	JIT_STUB(undefined_index_ex,        SP_ADJ_JIT,  SP_ADJ_VM),
2746	JIT_STUB(cannot_add_element_ex,     SP_ADJ_JIT,  SP_ADJ_VM),
2747	JIT_STUB(undefined_function,        SP_ADJ_JIT,  SP_ADJ_VM),
2748	JIT_STUB(negative_shift,            SP_ADJ_JIT,  SP_ADJ_VM),
2749	JIT_STUB(mod_by_zero,               SP_ADJ_JIT,  SP_ADJ_VM),
2750	JIT_STUB(invalid_this,              SP_ADJ_JIT,  SP_ADJ_VM),
2751	JIT_STUB(trace_halt,                SP_ADJ_JIT,  SP_ADJ_VM),
2752	JIT_STUB(trace_exit,                SP_ADJ_JIT,  SP_ADJ_VM),
2753	JIT_STUB(trace_escape,              SP_ADJ_JIT,  SP_ADJ_VM),
2754	JIT_STUB(hybrid_runtime_jit,        SP_ADJ_VM,   SP_ADJ_NONE),
2755	JIT_STUB(hybrid_profile_jit,        SP_ADJ_VM,   SP_ADJ_NONE),
2756	JIT_STUB(hybrid_hot_code,           SP_ADJ_VM,   SP_ADJ_NONE),
2757	JIT_STUB(hybrid_func_hot_counter,   SP_ADJ_VM,   SP_ADJ_NONE),
2758	JIT_STUB(hybrid_loop_hot_counter,   SP_ADJ_VM,   SP_ADJ_NONE),
2759	JIT_STUB(hybrid_hot_trace,          SP_ADJ_VM,   SP_ADJ_NONE),
2760	JIT_STUB(hybrid_func_trace_counter, SP_ADJ_VM,   SP_ADJ_NONE),
2761	JIT_STUB(hybrid_ret_trace_counter,  SP_ADJ_VM,   SP_ADJ_NONE),
2762	JIT_STUB(hybrid_loop_trace_counter, SP_ADJ_VM,   SP_ADJ_NONE),
2763	JIT_STUB(assign_const,              SP_ADJ_RET,  SP_ADJ_ASSIGN),
2764	JIT_STUB(assign_tmp,                SP_ADJ_RET,  SP_ADJ_ASSIGN),
2765	JIT_STUB(assign_var,                SP_ADJ_RET,  SP_ADJ_ASSIGN),
2766	JIT_STUB(assign_cv_noref,           SP_ADJ_RET,  SP_ADJ_ASSIGN),
2767	JIT_STUB(assign_cv,                 SP_ADJ_RET,  SP_ADJ_ASSIGN),
2768	JIT_STUB(double_one,                SP_ADJ_NONE, SP_ADJ_NONE),
2769#ifdef CONTEXT_THREADED_JIT
2770	JIT_STUB(context_threaded_call,     SP_ADJ_RET,  SP_ADJ_NONE),
2771#endif
2772};
2773
2774#if ZTS && defined(ZEND_WIN32)
2775extern uint32_t _tls_index;
2776extern char *_tls_start;
2777extern char *_tls_end;
2778#endif
2779
2780#ifdef HAVE_GDB
2781typedef struct _Unwind_Context _Unwind_Context;
2782typedef int (*_Unwind_Trace_Fn)(_Unwind_Context *, void *);
2783extern int _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
2784extern uintptr_t _Unwind_GetCFA(_Unwind_Context *);
2785
2786typedef struct _zend_jit_unwind_arg {
2787	int cnt;
2788	uintptr_t cfa[3];
2789} zend_jit_unwind_arg;
2790
2791static int zend_jit_unwind_cb(_Unwind_Context *ctx, void *a)
2792{
2793	zend_jit_unwind_arg *arg = (zend_jit_unwind_arg*)a;
2794	arg->cfa[arg->cnt] = _Unwind_GetCFA(ctx);
2795	arg->cnt++;
2796	if (arg->cnt == 3) {
2797		return 5; // _URC_END_OF_STACK
2798	}
2799	return 0; // _URC_NO_REASON;
2800}
2801
2802static void ZEND_FASTCALL zend_jit_touch_vm_stack_data(void *vm_stack_data)
2803{
2804	zend_jit_unwind_arg arg;
2805
2806	memset(&arg, 0, sizeof(arg));
2807	_Unwind_Backtrace(zend_jit_unwind_cb, &arg);
2808	if (arg.cnt == 3) {
2809		sp_adj[SP_ADJ_VM] = arg.cfa[2] - arg.cfa[1];
2810	}
2811}
2812
2813extern void (ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data);
2814
2815static zend_never_inline void zend_jit_set_sp_adj_vm(void)
2816{
2817	void (ZEND_FASTCALL *orig_zend_touch_vm_stack_data)(void *);
2818
2819	orig_zend_touch_vm_stack_data = zend_touch_vm_stack_data;
2820	zend_touch_vm_stack_data = zend_jit_touch_vm_stack_data;
2821	execute_ex(NULL);                                        // set sp_adj[SP_ADJ_VM]
2822	zend_touch_vm_stack_data = orig_zend_touch_vm_stack_data;
2823}
2824#endif
2825
2826static int zend_jit_setup(void)
2827{
2828	if (!zend_cpu_supports_sse2()) {
2829		zend_error(E_CORE_ERROR, "CPU doesn't support SSE2");
2830		return FAILURE;
2831	}
2832	allowed_opt_flags = 0;
2833	if (zend_cpu_supports_avx()) {
2834		allowed_opt_flags |= ZEND_JIT_CPU_AVX;
2835	}
2836
2837#if ZTS
2838# ifdef _WIN64
2839	tsrm_tls_index  = _tls_index * sizeof(void*);
2840
2841	/* To find offset of "_tsrm_ls_cache" in TLS segment we perform a linear scan of local TLS memory */
2842	/* Probably, it might be better solution */
2843	do {
2844		void ***tls_mem = ((void****)__readgsqword(0x58))[_tls_index];
2845		void *val = _tsrm_ls_cache;
2846		size_t offset = 0;
2847		size_t size = (char*)&_tls_end - (char*)&_tls_start;
2848
2849		while (offset < size) {
2850			if (*tls_mem == val) {
2851				tsrm_tls_offset = offset;
2852				break;
2853			}
2854			tls_mem++;
2855			offset += sizeof(void*);
2856		}
2857		if (offset >= size) {
2858			// TODO: error message ???
2859			return FAILURE;
2860		}
2861	} while(0);
2862# elif ZEND_WIN32
2863	tsrm_tls_index  = _tls_index * sizeof(void*);
2864
2865	/* To find offset of "_tsrm_ls_cache" in TLS segment we perform a linear scan of local TLS memory */
2866	/* Probably, it might be better solution */
2867	do {
2868		void ***tls_mem = ((void****)__readfsdword(0x2c))[_tls_index];
2869		void *val = _tsrm_ls_cache;
2870		size_t offset = 0;
2871		size_t size = (char*)&_tls_end - (char*)&_tls_start;
2872
2873		while (offset < size) {
2874			if (*tls_mem == val) {
2875				tsrm_tls_offset = offset;
2876				break;
2877			}
2878			tls_mem++;
2879			offset += sizeof(void*);
2880		}
2881		if (offset >= size) {
2882			// TODO: error message ???
2883			return FAILURE;
2884		}
2885	} while(0);
2886# elif defined(__APPLE__) && defined(__x86_64__)
2887	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
2888	if (tsrm_ls_cache_tcb_offset == 0) {
2889		size_t *ti;
2890		__asm__(
2891			"leaq __tsrm_ls_cache(%%rip),%0"
2892			: "=r" (ti));
2893		tsrm_tls_offset = ti[2];
2894		tsrm_tls_index = ti[1] * 8;
2895	}
2896# elif defined(__GNUC__) && defined(__x86_64__)
2897	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
2898	if (tsrm_ls_cache_tcb_offset == 0) {
2899#if defined(__has_attribute) && __has_attribute(tls_model) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__)
2900		size_t ret;
2901
2902		asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0"
2903			: "=r" (ret));
2904		tsrm_ls_cache_tcb_offset = ret;
2905#elif defined(__MUSL__)
2906		size_t *ti;
2907
2908		__asm__(
2909			"leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n"
2910			: "=a" (ti));
2911		tsrm_tls_offset = ti[1];
2912		tsrm_tls_index = ti[0] * 8;
2913#elif defined(__FreeBSD__)
2914		size_t *ti;
2915
2916		__asm__(
2917			"leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n"
2918			: "=a" (ti));
2919		tsrm_tls_offset = ti[1];
2920		/* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/bf56e8b9c8639ac4447d223b83cdc128107cc3cd/libexec/rtld-elf/rtld.c#L5260) */
2921		tsrm_tls_index = (ti[0] + 1) * 8;
2922#else
2923		size_t *ti;
2924
2925		__asm__(
2926			"leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n"
2927			: "=a" (ti));
2928		tsrm_tls_offset = ti[1];
2929		tsrm_tls_index = ti[0] * 16;
2930#endif
2931	}
2932# elif defined(__GNUC__) && defined(__i386__)
2933	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
2934	if (tsrm_ls_cache_tcb_offset == 0) {
2935#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__)
2936		size_t ret;
2937
2938		asm ("leal _tsrm_ls_cache@ntpoff,%0\n"
2939			: "=a" (ret));
2940		tsrm_ls_cache_tcb_offset = ret;
2941#else
2942		size_t *ti, _ebx, _ecx, _edx;
2943
2944		__asm__(
2945			"call 1f\n"
2946			".subsection 1\n"
2947			"1:\tmovl (%%esp), %%ebx\n\t"
2948			"ret\n"
2949			".previous\n\t"
2950			"addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t"
2951			"leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n\t"
2952			"call ___tls_get_addr@plt\n\t"
2953			"leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n"
2954			: "=a" (ti), "=&b" (_ebx), "=&c" (_ecx), "=&d" (_edx));
2955		tsrm_tls_offset = ti[1];
2956		tsrm_tls_index = ti[0] * 8;
2957#endif
2958	}
2959# endif
2960#endif
2961
2962    memset(sp_adj, 0, sizeof(sp_adj));
2963#ifdef HAVE_GDB
2964	sp_adj[SP_ADJ_RET] = sizeof(void*);
2965	|.if X64WIN
2966	||	sp_adj[SP_ADJ_ASSIGN] = sp_adj[SP_ADJ_RET] + 0x28;       // sub r4, 0x28
2967	|.elif X64
2968	||	sp_adj[SP_ADJ_ASSIGN] = sp_adj[SP_ADJ_RET] + 8;          // sub r4, 8
2969	|.else
2970	||	sp_adj[SP_ADJ_ASSIGN] = sp_adj[SP_ADJ_RET] + 12;         // sub r4, 12
2971	|.endif
2972	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2973		zend_jit_set_sp_adj_vm();                                // set sp_adj[SP_ADJ_VM]
2974#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
2975		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM] + HYBRID_SPAD; // sub r4, HYBRID_SPAD
2976#else
2977		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM];
2978#endif
2979	} else if (GCC_GLOBAL_REGS) {
2980		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + SPAD;       // sub r4, SPAD
2981	} else {
2982		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + NR_SPAD;    // sub r4, NR_SPAD
2983	}
2984#endif
2985
2986	return SUCCESS;
2987}
2988
2989static ZEND_ATTRIBUTE_UNUSED int zend_jit_trap(dasm_State **Dst)
2990{
2991	|	int3
2992	return 1;
2993}
2994
2995static int zend_jit_align_func(dasm_State **Dst)
2996{
2997	reuse_ip = 0;
2998	delayed_call_chain = 0;
2999	last_valid_opline = NULL;
3000	use_last_vald_opline = 0;
3001	track_last_valid_opline = 0;
3002	jit_return_label = -1;
3003	|.align 16
3004	return 1;
3005}
3006
3007static int zend_jit_align_stub(dasm_State **Dst)
3008{
3009	|.align 16
3010	return 1;
3011}
3012
3013static int zend_jit_prologue(dasm_State **Dst)
3014{
3015	|	ENDBR
3016	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3017		|	SUB_HYBRID_SPAD
3018	} else if (GCC_GLOBAL_REGS) {
3019		|	sub r4, SPAD // stack alignment
3020	} else {
3021		|	sub r4, NR_SPAD // stack alignment
3022		|	mov aword T2, FP // save FP
3023		|	mov aword T3, RX // save IP
3024		|	mov FP, FCARG1a
3025	}
3026	return 1;
3027}
3028
3029static int zend_jit_label(dasm_State **Dst, unsigned int label)
3030{
3031	|=>label:
3032	return 1;
3033}
3034
3035static int zend_jit_save_call_chain(dasm_State **Dst, uint32_t call_level)
3036{
3037	|	// call->prev_execute_data = EX(call);
3038	if (call_level == 1) {
3039		|	mov aword EX:RX->prev_execute_data, 0
3040	} else {
3041		|	mov r0, EX->call
3042		|	mov EX:RX->prev_execute_data, r0
3043	}
3044	|	// EX(call) = call;
3045	|	mov EX->call, RX
3046
3047	delayed_call_chain = 0;
3048
3049	return 1;
3050}
3051
3052static int zend_jit_set_ip(dasm_State **Dst, const zend_op *opline)
3053{
3054	if (last_valid_opline == opline) {
3055		zend_jit_use_last_valid_opline();
3056	} else if (GCC_GLOBAL_REGS && last_valid_opline) {
3057		zend_jit_use_last_valid_opline();
3058		|	ADD_IP (opline - last_valid_opline) * sizeof(zend_op);
3059	} else {
3060		|	LOAD_IP_ADDR opline
3061	}
3062	zend_jit_set_last_valid_opline(opline);
3063
3064	return 1;
3065}
3066
3067static int zend_jit_set_ip_ex(dasm_State **Dst, const zend_op *opline, bool set_ip_reg)
3068{
3069	if (last_valid_opline == opline) {
3070		zend_jit_use_last_valid_opline();
3071	} else if (GCC_GLOBAL_REGS && last_valid_opline) {
3072		zend_jit_use_last_valid_opline();
3073		|	ADD_IP (opline - last_valid_opline) * sizeof(zend_op);
3074	} else if (!GCC_GLOBAL_REGS && set_ip_reg) {
3075		|	LOAD_ADDR RX, opline
3076		|	mov aword EX->opline, RX
3077	} else {
3078		|	LOAD_IP_ADDR opline
3079	}
3080	zend_jit_set_last_valid_opline(opline);
3081
3082	return 1;
3083}
3084
3085static int zend_jit_set_valid_ip(dasm_State **Dst, const zend_op *opline)
3086{
3087	if (delayed_call_chain) {
3088		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
3089			return 0;
3090		}
3091	}
3092	if (!zend_jit_set_ip(Dst, opline)) {
3093		return 0;
3094	}
3095	reuse_ip = 0;
3096	return 1;
3097}
3098
3099static int zend_jit_check_timeout(dasm_State **Dst, const zend_op *opline, const void *exit_addr)
3100{
3101#if 0
3102	if (!zend_jit_set_valid_ip(Dst, opline)) {
3103		return 0;
3104	}
3105	|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3106	|	jne ->interrupt_handler
3107#else
3108	|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3109	if (exit_addr) {
3110		|	jne &exit_addr
3111	} else if (last_valid_opline == opline) {
3112		||		zend_jit_use_last_valid_opline();
3113		|	jne ->interrupt_handler
3114	} else {
3115		|	jne >1
3116		|.cold_code
3117		|1:
3118		|	LOAD_IP_ADDR opline
3119		|	jmp ->interrupt_handler
3120		|.code
3121	}
3122#endif
3123	return 1;
3124}
3125
3126static int zend_jit_trace_end_loop(dasm_State **Dst, int loop_label, const void *timeout_exit_addr)
3127{
3128	if (timeout_exit_addr) {
3129		|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3130		|	je =>loop_label
3131		|	jmp &timeout_exit_addr
3132	} else {
3133		|	jmp =>loop_label
3134	}
3135	return 1;
3136}
3137
3138static int zend_jit_check_exception(dasm_State **Dst)
3139{
3140	|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
3141	|	jne ->exception_handler
3142	return 1;
3143}
3144
3145static int zend_jit_check_exception_undef_result(dasm_State **Dst, const zend_op *opline)
3146{
3147	if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
3148		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
3149		|	jne ->exception_handler_undef
3150		return 1;
3151	}
3152	return zend_jit_check_exception(Dst);
3153}
3154
3155static int zend_jit_trace_begin(dasm_State **Dst, uint32_t trace_num, zend_jit_trace_info *parent, uint32_t exit_num)
3156{
3157	zend_regset regset = ZEND_REGSET_SCRATCH;
3158
3159#if ZTS
3160	if (1) {
3161#else
3162	if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(jit_trace_num)))) {
3163#endif
3164		/* assignment to EG(jit_trace_num) shouldn't clober CPU register used by deoptimizer */
3165		if (parent) {
3166			int i;
3167			int parent_vars_count = parent->exit_info[exit_num].stack_size;
3168			zend_jit_trace_stack *parent_stack =
3169				parent->stack_map +
3170				parent->exit_info[exit_num].stack_offset;
3171
3172			for (i = 0; i < parent_vars_count; i++) {
3173				if (STACK_REG(parent_stack, i) != ZREG_NONE) {
3174					if (STACK_REG(parent_stack, i) < ZREG_NUM) {
3175						ZEND_REGSET_EXCL(regset, STACK_REG(parent_stack, i));
3176					} else if (STACK_REG(parent_stack, i) == ZREG_ZVAL_COPY_GPR0) {
3177						ZEND_REGSET_EXCL(regset, ZREG_R0);
3178					}
3179				}
3180			}
3181		}
3182	}
3183
3184	if (parent && parent->exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL) {
3185		ZEND_REGSET_EXCL(regset, ZREG_R0);
3186	}
3187
3188	current_trace_num = trace_num;
3189
3190	|	// EG(jit_trace_num) = trace_num;
3191	if (regset == ZEND_REGSET_EMPTY) {
3192		|	push r0
3193		|	MEM_STORE_ZTS dword, executor_globals, jit_trace_num, trace_num, r0
3194		|	pop r0
3195	} else {
3196		zend_reg tmp = ZEND_REGSET_FIRST(regset);
3197
3198		|	MEM_STORE_ZTS dword, executor_globals, jit_trace_num, trace_num, Ra(tmp)
3199		(void)tmp;
3200	}
3201
3202	return 1;
3203}
3204
3205static int zend_jit_trace_end(dasm_State **Dst, zend_jit_trace_info *t)
3206{
3207	|.cold_code
3208	|=>1: // end of the code
3209	|.code
3210	return 1;
3211}
3212
3213/* This taken from LuaJIT. Thanks to Mike Pall. */
3214static uint32_t _asm_x86_inslen(const uint8_t* p)
3215{
3216	static const uint8_t map_op1[256] = {
3217		0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x20,
3218		0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,
3219		0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,
3220		0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,
3221#if defined(__x86_64__) || defined(_M_X64)
3222		0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,
3223#else
3224		0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,
3225#endif
3226		0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,
3227		0x51,0x51,0x92,0x92,0x10,0x10,0x12,0x11,0x45,0x86,0x52,0x93,0x51,0x51,0x51,0x51,
3228		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,
3229		0x93,0x86,0x93,0x93,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,
3230		0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x47,0x51,0x51,0x51,0x51,0x51,
3231#if defined(__x86_64__) || defined(_M_X64)
3232		0x59,0x59,0x59,0x59,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51,
3233#else
3234		0x55,0x55,0x55,0x55,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51,
3235#endif
3236		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,
3237		0x93,0x93,0x53,0x51,0x70,0x71,0x93,0x86,0x54,0x51,0x53,0x51,0x51,0x52,0x51,0x51,
3238		0x92,0x92,0x92,0x92,0x52,0x52,0x51,0x51,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,
3239		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x45,0x45,0x47,0x52,0x51,0x51,0x51,0x51,
3240		0x10,0x51,0x10,0x10,0x51,0x51,0x63,0x66,0x51,0x51,0x51,0x51,0x51,0x51,0x92,0x92
3241	};
3242	static const uint8_t map_op2[256] = {
3243		0x93,0x93,0x93,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x51,0x52,0x51,0x93,0x52,0x94,
3244		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3245		0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3246		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x34,0x51,0x35,0x51,0x51,0x51,0x51,0x51,
3247		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3248		0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3249		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3250		0x94,0x54,0x54,0x54,0x93,0x93,0x93,0x52,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3251		0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,
3252		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3253		0x52,0x52,0x52,0x93,0x94,0x93,0x51,0x51,0x52,0x52,0x52,0x93,0x94,0x93,0x93,0x93,
3254		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x94,0x93,0x93,0x93,0x93,0x93,
3255		0x93,0x93,0x94,0x93,0x94,0x94,0x94,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,
3256		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3257		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3258		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x52
3259	};
3260	uint32_t result = 0;
3261	uint32_t prefixes = 0;
3262	uint32_t x = map_op1[*p];
3263
3264	for (;;) {
3265		switch (x >> 4) {
3266			case 0:
3267				return result + x + (prefixes & 4);
3268			case 1:
3269				prefixes |= x;
3270				x = map_op1[*++p];
3271				result++;
3272				break;
3273			case 2:
3274				x = map_op2[*++p];
3275				break;
3276			case 3:
3277				p++;
3278				goto mrm;
3279			case 4:
3280				result -= (prefixes & 2);
3281				/* fallthrough */
3282			case 5:
3283				return result + (x & 15);
3284			case 6: /* Group 3. */
3285				if (p[1] & 0x38) {
3286					x = 2;
3287				} else if ((prefixes & 2) && (x == 0x66)) {
3288					x = 4;
3289				}
3290				goto mrm;
3291			case 7: /* VEX c4/c5. */
3292#if !defined(__x86_64__) && !defined(_M_X64)
3293				if (p[1] < 0xc0) {
3294					x = 2;
3295					goto mrm;
3296				}
3297#endif
3298				if (x == 0x70) {
3299					x = *++p & 0x1f;
3300					result++;
3301					if (x >= 2) {
3302						p += 2;
3303						result += 2;
3304						goto mrm;
3305					}
3306				}
3307				p++;
3308				result++;
3309				x = map_op2[*++p];
3310				break;
3311			case 8:
3312				result -= (prefixes & 2);
3313				/* fallthrough */
3314			case 9:
3315mrm:
3316				/* ModR/M and possibly SIB. */
3317				result += (x & 15);
3318				x = *++p;
3319				switch (x >> 6) {
3320					case 0:
3321						if ((x & 7) == 5) {
3322							return result + 4;
3323						}
3324						break;
3325					case 1:
3326						result++;
3327						break;
3328					case 2:
3329						result += 4;
3330						break;
3331					case 3:
3332						return result;
3333				}
3334				if ((x & 7) == 4) {
3335					result++;
3336					if (x < 0x40 && (p[1] & 7) == 5) {
3337						result += 4;
3338					}
3339				}
3340				return result;
3341		}
3342	}
3343}
3344
3345typedef ZEND_SET_ALIGNED(1, uint16_t unaligned_uint16_t);
3346typedef ZEND_SET_ALIGNED(1, int32_t unaligned_int32_t);
3347
3348static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size, const void *from_addr, const void *to_addr)
3349{
3350	int ret = 0;
3351	uint8_t *p, *end;
3352
3353	if (jmp_table_size) {
3354		const void **jmp_slot = (const void **)((char*)code + ZEND_MM_ALIGNED_SIZE_EX(size, sizeof(void*)));
3355
3356		do {
3357			if (*jmp_slot == from_addr) {
3358				*jmp_slot = to_addr;
3359				ret++;
3360			}
3361			jmp_slot++;
3362		} while (--jmp_table_size);
3363	}
3364
3365	p = (uint8_t*)code;
3366	end = p + size - 5;
3367	while (p < end) {
3368		if ((*(unaligned_uint16_t*)p & 0xf0ff) == 0x800f && p + *(unaligned_int32_t*)(p+2) == (uint8_t*)from_addr - 6) {
3369			*(unaligned_int32_t*)(p+2) = ((uint8_t*)to_addr - (p + 6));
3370			ret++;
3371		} else if (*p == 0xe9 && p + *(unaligned_int32_t*)(p+1) == (uint8_t*)from_addr - 5) {
3372			*(unaligned_int32_t*)(p+1) = ((uint8_t*)to_addr - (p + 5));
3373			ret++;
3374		}
3375		p += _asm_x86_inslen(p);
3376	}
3377#ifdef HAVE_VALGRIND
3378	VALGRIND_DISCARD_TRANSLATIONS(code, size);
3379#endif
3380	return ret;
3381}
3382
3383static int zend_jit_link_side_trace(const void *code, size_t size, uint32_t jmp_table_size, uint32_t exit_num, const void *addr)
3384{
3385	return zend_jit_patch(code, size, jmp_table_size, zend_jit_trace_get_exit_addr(exit_num), addr);
3386}
3387
3388static int zend_jit_trace_link_to_root(dasm_State **Dst, zend_jit_trace_info *t, const void *timeout_exit_addr)
3389{
3390	const void *link_addr;
3391	size_t prologue_size;
3392
3393	/* Skip prologue. */
3394	// TODO: don't hardcode this ???
3395	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3396#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
3397		prologue_size = 0;
3398#elif defined(__x86_64__) || defined(_M_X64)
3399		// sub r4, HYBRID_SPAD
3400		prologue_size = 4;
3401#else
3402		// sub r4, HYBRID_SPAD
3403		prologue_size = 3;
3404#endif
3405	} else if (GCC_GLOBAL_REGS) {
3406		// sub r4, SPAD // stack alignment
3407#if defined(__x86_64__) || defined(_M_X64)
3408		prologue_size = 4;
3409#else
3410		prologue_size = 3;
3411#endif
3412	} else {
3413		// sub r4, NR_SPAD // stack alignment
3414		// mov aword T2, FP // save FP
3415		// mov aword T3, RX // save IP
3416		// mov FP, FCARG1a
3417#if defined(__x86_64__) || defined(_M_X64)
3418		prologue_size = 17;
3419#else
3420		prologue_size = 13;
3421#endif
3422	}
3423	link_addr = (const void*)((const char*)t->code_start + prologue_size + ENDBR_PADDING);
3424
3425	if (timeout_exit_addr) {
3426		/* Check timeout for links to LOOP */
3427		|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3428		|	je &link_addr
3429		|	jmp &timeout_exit_addr
3430	} else {
3431		|	jmp &link_addr
3432	}
3433	return 1;
3434}
3435
3436static int zend_jit_trace_return(dasm_State **Dst, bool original_handler, const zend_op *opline)
3437{
3438#if 0
3439	|	jmp ->trace_escape
3440#else
3441	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3442		|	ADD_HYBRID_SPAD
3443		if (!original_handler) {
3444			|	JMP_IP
3445		} else {
3446			|	mov r0, EX->func
3447			|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
3448			|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
3449			|	jmp aword [IP + r0]
3450		}
3451	} else if (GCC_GLOBAL_REGS) {
3452		|	add r4, SPAD // stack alignment
3453		if (!original_handler) {
3454			|	JMP_IP
3455		} else {
3456			|	mov r0, EX->func
3457			|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
3458			|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
3459			|	jmp aword [IP + r0]
3460		}
3461	} else {
3462		if (original_handler) {
3463			|	mov FCARG1a, FP
3464			|	mov r0, EX->func
3465			|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
3466			|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
3467			|	call aword [IP + r0]
3468		}
3469		|	mov FP, aword T2 // restore FP
3470		|	mov RX, aword T3 // restore IP
3471		|	add r4, NR_SPAD // stack alignment
3472		if (!original_handler || !opline ||
3473		    (opline->opcode != ZEND_RETURN
3474		  && opline->opcode != ZEND_RETURN_BY_REF
3475		  && opline->opcode != ZEND_GENERATOR_RETURN
3476		  && opline->opcode != ZEND_GENERATOR_CREATE
3477		  && opline->opcode != ZEND_YIELD
3478		  && opline->opcode != ZEND_YIELD_FROM)) {
3479			|	mov r0, 2 // ZEND_VM_LEAVE
3480		}
3481		|	ret
3482	}
3483#endif
3484	return 1;
3485}
3486
3487static int zend_jit_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint8_t type)
3488{
3489	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
3490	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3491
3492	if (!exit_addr) {
3493		return 0;
3494	}
3495	|	IF_NOT_Z_TYPE FP + var, type, &exit_addr
3496
3497	return 1;
3498}
3499
3500static int zend_jit_scalar_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var)
3501{
3502	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
3503	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3504
3505	if (!exit_addr) {
3506		return 0;
3507	}
3508	|	cmp byte [FP+var+offsetof(zval, u1.v.type)], IS_STRING
3509	|	jae &exit_addr
3510
3511	return 1;
3512}
3513
3514static int zend_jit_packed_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint32_t op_info)
3515{
3516	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
3517	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3518
3519	if (!exit_addr) {
3520		return 0;
3521	}
3522
3523	|	GET_ZVAL_LVAL ZREG_FCARG1, ZEND_ADDR_MEM_ZVAL(ZREG_FP, var)
3524	if (op_info & MAY_BE_ARRAY_PACKED) {
3525		|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
3526		|	jz &exit_addr
3527	} else {
3528		|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
3529		|	jnz &exit_addr
3530	}
3531
3532	return 1;
3533}
3534
3535static 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)
3536{
3537	zend_jit_op_array_trace_extension *jit_extension =
3538		(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
3539	size_t offset = jit_extension->offset;
3540	const void *handler =
3541		(zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler;
3542
3543	if (!zend_jit_set_valid_ip(Dst, opline)) {
3544		return 0;
3545	}
3546	if (!GCC_GLOBAL_REGS) {
3547		|	mov FCARG1a, FP
3548	}
3549	|	EXT_CALL handler, r0
3550	if (may_throw
3551	 && opline->opcode != ZEND_RETURN
3552	 && opline->opcode != ZEND_RETURN_BY_REF) {
3553		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r1
3554		|	jne ->exception_handler
3555	}
3556
3557	while (trace->op != ZEND_JIT_TRACE_VM && trace->op != ZEND_JIT_TRACE_END) {
3558		trace++;
3559	}
3560
3561	if (!GCC_GLOBAL_REGS
3562	 && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_RETURN)) {
3563		if (opline->opcode == ZEND_RETURN ||
3564		    opline->opcode == ZEND_RETURN_BY_REF ||
3565		    opline->opcode == ZEND_DO_UCALL ||
3566		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
3567		    opline->opcode == ZEND_DO_FCALL ||
3568		    opline->opcode == ZEND_GENERATOR_CREATE) {
3569			|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r1
3570		}
3571	}
3572
3573	if (zend_jit_trace_may_exit(op_array, opline)) {
3574		if (opline->opcode == ZEND_RETURN ||
3575		    opline->opcode == ZEND_RETURN_BY_REF ||
3576		    opline->opcode == ZEND_GENERATOR_CREATE) {
3577
3578			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3579				if (trace->op != ZEND_JIT_TRACE_END ||
3580				    (trace->stop != ZEND_JIT_TRACE_STOP_RETURN &&
3581				     trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
3582					/* this check may be handled by the following OPLINE guard or jmp [IP] */
3583					|	cmp IP, zend_jit_halt_op
3584					|	je ->trace_halt
3585				}
3586			} else if (GCC_GLOBAL_REGS) {
3587				|	test IP, IP
3588				|	je ->trace_halt
3589			} else {
3590				|	test eax, eax
3591				|	jl ->trace_halt
3592			}
3593		} else if (opline->opcode == ZEND_EXIT ||
3594		           opline->opcode == ZEND_GENERATOR_RETURN ||
3595		           opline->opcode == ZEND_YIELD ||
3596		           opline->opcode == ZEND_YIELD_FROM) {
3597			|	jmp ->trace_halt
3598		}
3599		if (trace->op != ZEND_JIT_TRACE_END ||
3600		    (trace->stop != ZEND_JIT_TRACE_STOP_RETURN &&
3601		     trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
3602
3603			const zend_op *next_opline = trace->opline;
3604			const zend_op *exit_opline = NULL;
3605			uint32_t exit_point;
3606			const void *exit_addr;
3607			uint32_t old_info = 0;
3608			uint32_t old_res_info = 0;
3609			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
3610
3611			if (zend_is_smart_branch(opline)) {
3612				bool exit_if_true = 0;
3613				exit_opline = zend_jit_trace_get_exit_opline(trace, opline + 1, &exit_if_true);
3614			} else {
3615				switch (opline->opcode) {
3616					case ZEND_JMPZ:
3617					case ZEND_JMPNZ:
3618					case ZEND_JMPZ_EX:
3619					case ZEND_JMPNZ_EX:
3620					case ZEND_JMP_SET:
3621					case ZEND_COALESCE:
3622					case ZEND_JMP_NULL:
3623					case ZEND_FE_RESET_R:
3624					case ZEND_FE_RESET_RW:
3625						exit_opline = (trace->opline == opline + 1) ?
3626							OP_JMP_ADDR(opline, opline->op2) :
3627							opline + 1;
3628						break;
3629					case ZEND_FE_FETCH_R:
3630					case ZEND_FE_FETCH_RW:
3631						exit_opline = (trace->opline == opline + 1) ?
3632							ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
3633							opline + 1;
3634						break;
3635
3636				}
3637			}
3638
3639			switch (opline->opcode) {
3640				case ZEND_FE_FETCH_R:
3641				case ZEND_FE_FETCH_RW:
3642					if (opline->op2_type != IS_UNUSED) {
3643						old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var));
3644						SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1);
3645					}
3646					break;
3647				case ZEND_BIND_INIT_STATIC_OR_JMP:
3648					if (opline->op1_type == IS_CV) {
3649						old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
3650						SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_UNKNOWN, 1);
3651					}
3652					break;
3653			}
3654
3655			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
3656				old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
3657				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
3658			}
3659			exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
3660			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3661
3662			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
3663				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
3664			}
3665			switch (opline->opcode) {
3666				case ZEND_FE_FETCH_R:
3667				case ZEND_FE_FETCH_RW:
3668					if (opline->op2_type != IS_UNUSED) {
3669						SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var), old_info);
3670					}
3671					break;
3672				case ZEND_BIND_INIT_STATIC_OR_JMP:
3673					if (opline->op1_type == IS_CV) {
3674						SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_info);
3675					}
3676					break;
3677			}
3678
3679			if (!exit_addr) {
3680				return 0;
3681			}
3682			|	CMP_IP next_opline
3683			|	jne &exit_addr
3684		}
3685	}
3686
3687	zend_jit_set_last_valid_opline(trace->opline);
3688
3689	return 1;
3690}
3691
3692static int zend_jit_handler(dasm_State **Dst, const zend_op *opline, int may_throw)
3693{
3694	const void *handler;
3695
3696	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3697		handler = zend_get_opcode_handler_func(opline);
3698	} else {
3699		handler = opline->handler;
3700	}
3701
3702	if (!zend_jit_set_valid_ip(Dst, opline)) {
3703		return 0;
3704	}
3705	if (!GCC_GLOBAL_REGS) {
3706		|	mov FCARG1a, FP
3707	}
3708	|	EXT_CALL handler, r0
3709	if (may_throw) {
3710		zend_jit_check_exception(Dst);
3711	}
3712
3713	/* Skip the following OP_DATA */
3714	switch (opline->opcode) {
3715		case ZEND_ASSIGN_DIM:
3716		case ZEND_ASSIGN_OBJ:
3717		case ZEND_ASSIGN_STATIC_PROP:
3718		case ZEND_ASSIGN_DIM_OP:
3719		case ZEND_ASSIGN_OBJ_OP:
3720		case ZEND_ASSIGN_STATIC_PROP_OP:
3721		case ZEND_ASSIGN_STATIC_PROP_REF:
3722		case ZEND_ASSIGN_OBJ_REF:
3723			zend_jit_set_last_valid_opline(opline + 2);
3724			break;
3725		default:
3726			zend_jit_set_last_valid_opline(opline + 1);
3727			break;
3728	}
3729
3730	return 1;
3731}
3732
3733static int zend_jit_tail_handler(dasm_State **Dst, const zend_op *opline)
3734{
3735	if (!zend_jit_set_valid_ip(Dst, opline)) {
3736		return 0;
3737	}
3738	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3739		if (opline->opcode == ZEND_DO_UCALL ||
3740		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
3741		    opline->opcode == ZEND_DO_FCALL ||
3742		    opline->opcode == ZEND_RETURN) {
3743
3744			/* Use inlined HYBRID VM handler */
3745			const void *handler = opline->handler;
3746
3747			|	ADD_HYBRID_SPAD
3748			|	EXT_JMP handler, r0
3749		} else {
3750			const void *handler = zend_get_opcode_handler_func(opline);
3751
3752			|	EXT_CALL handler, r0
3753			|	ADD_HYBRID_SPAD
3754			|	JMP_IP
3755		}
3756	} else {
3757		const void *handler = opline->handler;
3758
3759		if (GCC_GLOBAL_REGS) {
3760			|	add r4, SPAD // stack alignment
3761		} else {
3762			|	mov FCARG1a, FP
3763			|	mov FP, aword T2 // restore FP
3764			|	mov RX, aword T3 // restore IP
3765			|	add r4, NR_SPAD // stack alignment
3766		}
3767		|	EXT_JMP handler, r0
3768	}
3769	zend_jit_reset_last_valid_opline();
3770	return 1;
3771}
3772
3773static int zend_jit_trace_opline_guard(dasm_State **Dst, const zend_op *opline)
3774{
3775	uint32_t exit_point = zend_jit_trace_get_exit_point(NULL, 0);
3776	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3777
3778	if (!exit_addr) {
3779		return 0;
3780	}
3781	|	CMP_IP opline
3782	|	jne &exit_addr
3783
3784	zend_jit_set_last_valid_opline(opline);
3785
3786	return 1;
3787}
3788
3789static int zend_jit_jmp(dasm_State **Dst, unsigned int target_label)
3790{
3791	|	jmp =>target_label
3792	return 1;
3793}
3794
3795static int zend_jit_cond_jmp(dasm_State **Dst, const zend_op *next_opline, unsigned int target_label)
3796{
3797	|	CMP_IP next_opline
3798	|	jne =>target_label
3799
3800	zend_jit_set_last_valid_opline(next_opline);
3801
3802	return 1;
3803}
3804
3805#ifdef CONTEXT_THREADED_JIT
3806static int zend_jit_context_threaded_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
3807{
3808	if (!zend_jit_handler(Dst, opline, 1)) return 0;
3809	if (opline->opcode == ZEND_DO_UCALL) {
3810		|	call ->context_threaded_call
3811	} else {
3812		const zend_op *next_opline = opline + 1;
3813
3814		|	CMP_IP next_opline
3815		|	je =>next_block
3816		|	call ->context_threaded_call
3817	}
3818	return 1;
3819}
3820#endif
3821
3822static int zend_jit_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
3823{
3824#ifdef CONTEXT_THREADED_JIT
3825	return zend_jit_context_threaded_call(Dst, opline, next_block);
3826#else
3827	return zend_jit_tail_handler(Dst, opline);
3828#endif
3829}
3830
3831static int zend_jit_spill_store(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info, bool set_type)
3832{
3833	ZEND_ASSERT(Z_MODE(src) == IS_REG);
3834	ZEND_ASSERT(Z_MODE(dst) == IS_MEM_ZVAL);
3835
3836	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3837		|	SET_ZVAL_LVAL dst, Ra(Z_REG(src))
3838		if (set_type &&
3839		    (Z_REG(dst) != ZREG_FP ||
3840		     !JIT_G(current_frame) ||
3841		     STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_LONG)) {
3842			|	SET_ZVAL_TYPE_INFO dst, IS_LONG
3843		}
3844	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3845		|	DOUBLE_SET_ZVAL_DVAL dst, Z_REG(src)
3846		if (set_type &&
3847		    (Z_REG(dst) != ZREG_FP ||
3848		     !JIT_G(current_frame) ||
3849		     STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_DOUBLE)) {
3850			|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
3851		}
3852	} else {
3853		ZEND_UNREACHABLE();
3854	}
3855	return 1;
3856}
3857
3858static int zend_jit_load_reg(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
3859{
3860	ZEND_ASSERT(Z_MODE(src) == IS_MEM_ZVAL);
3861	ZEND_ASSERT(Z_MODE(dst) == IS_REG);
3862
3863	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3864		|	GET_ZVAL_LVAL Z_REG(dst), src
3865	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3866		|	DOUBLE_GET_ZVAL_DVAL Z_REG(dst), src
3867	} else {
3868		ZEND_UNREACHABLE();
3869	}
3870	return 1;
3871}
3872
3873static int zend_jit_store_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg, bool set_type)
3874{
3875	zend_jit_addr src = ZEND_ADDR_REG(reg);
3876	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3877
3878	return zend_jit_spill_store(Dst, src, dst, info, set_type);
3879}
3880
3881static int zend_jit_store_var_type(dasm_State **Dst, int var, uint32_t type)
3882{
3883	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3884
3885	|	SET_ZVAL_TYPE_INFO dst, type
3886	return 1;
3887}
3888
3889static int zend_jit_store_var_if_necessary(dasm_State **Dst, int var, zend_jit_addr src, uint32_t info)
3890{
3891	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
3892		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3893		return zend_jit_spill_store(Dst, src, dst, info, 1);
3894	}
3895	return 1;
3896}
3897
3898static 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)
3899{
3900	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
3901		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3902		bool set_type = 1;
3903
3904		if ((info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) ==
3905		    (old_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF))) {
3906			if (Z_MODE(old) != IS_REG || Z_LOAD(old) || Z_STORE(old)) {
3907				set_type = 0;
3908			}
3909		}
3910		return zend_jit_spill_store(Dst, src, dst, info, set_type);
3911	}
3912	return 1;
3913}
3914
3915static int zend_jit_load_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg)
3916{
3917	zend_jit_addr src = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3918	zend_jit_addr dst = ZEND_ADDR_REG(reg);
3919
3920	return zend_jit_load_reg(Dst, src, dst, info);
3921}
3922
3923static int zend_jit_invalidate_var_if_necessary(dasm_State **Dst, uint8_t op_type, zend_jit_addr addr, znode_op op)
3924{
3925	if ((op_type & (IS_TMP_VAR|IS_VAR)) && Z_MODE(addr) == IS_REG && !Z_LOAD(addr) && !Z_STORE(addr)) {
3926		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var);
3927		|	SET_ZVAL_TYPE_INFO dst, IS_UNDEF
3928	}
3929	return 1;
3930}
3931
3932static int zend_jit_update_regs(dasm_State **Dst, uint32_t var, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
3933{
3934	if (!zend_jit_same_addr(src, dst)) {
3935		if (Z_MODE(src) == IS_REG) {
3936			if (Z_MODE(dst) == IS_REG) {
3937				if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3938					|	mov Ra(Z_REG(dst)), Ra(Z_REG(src))
3939				} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3940					|	SSE_AVX_INS movaps, vmovaps, xmm(Z_REG(dst)-ZREG_XMM0), xmm(Z_REG(src)-ZREG_XMM0)
3941				} else {
3942					ZEND_UNREACHABLE();
3943				}
3944				if (!Z_LOAD(src) && !Z_STORE(src) && Z_STORE(dst)) {
3945					zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3946
3947					if (!zend_jit_spill_store(Dst, dst, var_addr, info,
3948							JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3949							JIT_G(current_frame) == NULL ||
3950							STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3951							(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3952					)) {
3953						return 0;
3954					}
3955				}
3956			} else if (Z_MODE(dst) == IS_MEM_ZVAL) {
3957				if (!Z_LOAD(src) && !Z_STORE(src)) {
3958					if (!zend_jit_spill_store(Dst, src, dst, info,
3959							JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3960							JIT_G(current_frame) == NULL ||
3961							STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3962							(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3963					)) {
3964						return 0;
3965					}
3966				}
3967			} else {
3968				ZEND_UNREACHABLE();
3969			}
3970		} else if (Z_MODE(src) == IS_MEM_ZVAL) {
3971			if (Z_MODE(dst) == IS_REG) {
3972				if (!zend_jit_load_reg(Dst, src, dst, info)) {
3973					return 0;
3974				}
3975			} else {
3976				ZEND_UNREACHABLE();
3977			}
3978		} else {
3979			ZEND_UNREACHABLE();
3980		}
3981	} else if (Z_MODE(dst) == IS_REG && Z_STORE(dst)) {
3982		dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3983		if (!zend_jit_spill_store(Dst, src, dst, info,
3984				JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3985				JIT_G(current_frame) == NULL ||
3986				STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3987				(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3988		)) {
3989			return 0;
3990		}
3991	}
3992	return 1;
3993}
3994
3995static int zend_jit_escape_if_undef_r0(dasm_State **Dst, int var, uint32_t flags, const zend_op *opline)
3996{
3997	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
3998
3999	|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >1
4000
4001	if (flags & ZEND_JIT_EXIT_RESTORE_CALL) {
4002		if (!zend_jit_save_call_chain(Dst, -1)) {
4003			return 0;
4004		}
4005	}
4006
4007	ZEND_ASSERT(opline);
4008
4009	if ((opline-1)->opcode != ZEND_FETCH_CONSTANT
4010	 && (opline-1)->opcode != ZEND_FETCH_LIST_R
4011	 && ((opline-1)->op1_type & (IS_VAR|IS_TMP_VAR))
4012	 && !(flags & ZEND_JIT_EXIT_FREE_OP1)) {
4013		val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline-1)->op1.var);
4014
4015		|	IF_NOT_ZVAL_REFCOUNTED val_addr, >2
4016		|	GET_ZVAL_PTR r0, val_addr
4017		|	GC_ADDREF r0
4018		|2:
4019	}
4020
4021	|	LOAD_IP_ADDR (opline - 1)
4022	|	jmp ->trace_escape
4023	|1:
4024
4025	return 1;
4026}
4027
4028static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
4029{
4030	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
4031
4032	if (reg == ZREG_LONG_MIN_MINUS_1) {
4033		|.if X64
4034			|	SET_ZVAL_LVAL dst, 0x00000000
4035			|	SET_ZVAL_W2 dst, 0xc3e00000
4036		|.else
4037			|	SET_ZVAL_LVAL dst, 0x00200000
4038			|	SET_ZVAL_W2 dst, 0xc1e00000
4039		|.endif
4040		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
4041	} else if (reg == ZREG_LONG_MIN) {
4042		|.if X64
4043			|	SET_ZVAL_LVAL dst, 0x00000000
4044			|	SET_ZVAL_W2 dst, 0x80000000
4045		|.else
4046			|	SET_ZVAL_LVAL dst, ZEND_LONG_MIN
4047		|.endif
4048		|	SET_ZVAL_TYPE_INFO dst, IS_LONG
4049	} else if (reg == ZREG_LONG_MAX) {
4050		|.if X64
4051			|	SET_ZVAL_LVAL dst, 0xffffffff
4052			|	SET_ZVAL_W2 dst, 0x7fffffff
4053		|.else
4054			|	SET_ZVAL_LVAL dst, ZEND_LONG_MAX
4055		|.endif
4056		|	SET_ZVAL_TYPE_INFO dst, IS_LONG
4057	} else if (reg == ZREG_LONG_MAX_PLUS_1) {
4058		|.if X64
4059			|	SET_ZVAL_LVAL dst, 0
4060			|	SET_ZVAL_W2 dst, 0x43e00000
4061		|.else
4062			|	SET_ZVAL_LVAL dst, 0
4063			|	SET_ZVAL_W2 dst, 0x41e00000
4064		|.endif
4065		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
4066	} else if (reg == ZREG_NULL) {
4067		|	SET_ZVAL_TYPE_INFO dst, IS_NULL
4068	} else if (reg == ZREG_ZVAL_TRY_ADDREF) {
4069		|	IF_NOT_ZVAL_REFCOUNTED dst, >1
4070		|	GET_ZVAL_PTR r1, dst
4071		|	GC_ADDREF r1
4072		|1:
4073	} else if (reg == ZREG_ZVAL_COPY_GPR0) {
4074		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
4075
4076		|	ZVAL_COPY_VALUE dst, -1, val_addr, -1, ZREG_R1, ZREG_R2
4077		|	TRY_ADDREF -1, ch, r2
4078	} else {
4079		ZEND_UNREACHABLE();
4080	}
4081	return 1;
4082}
4083
4084static int zend_jit_free_trampoline(dasm_State **Dst)
4085{
4086	|	/// if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
4087	|	test dword [r0 + offsetof(zend_function, common.fn_flags)], ZEND_ACC_CALL_VIA_TRAMPOLINE
4088	|	jz >1
4089	|	mov FCARG1a, r0
4090	|	EXT_CALL zend_jit_free_trampoline_helper, r0
4091	|1:
4092	return 1;
4093}
4094
4095static 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)
4096{
4097	if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_LONG)) {
4098		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2
4099	}
4100	if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
4101		|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4102	}
4103	if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, MAY_BE_LONG)) {
4104		return 0;
4105	}
4106	if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4107		|	LONG_OP_WITH_32BIT_CONST add, op1_def_addr, Z_L(1)
4108	} else {
4109		|	LONG_OP_WITH_32BIT_CONST sub, op1_def_addr, Z_L(1)
4110	}
4111
4112	if (may_overflow &&
4113	    (((op1_def_info & MAY_BE_GUARD) && (op1_def_info & MAY_BE_LONG)) ||
4114	     ((opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD) && (res_info & MAY_BE_LONG))))) {
4115		int32_t exit_point;
4116		const void *exit_addr;
4117		zend_jit_trace_stack *stack;
4118		uint32_t old_op1_info, old_res_info = 0;
4119
4120		stack = JIT_G(current_frame)->stack;
4121		old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
4122		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_DOUBLE, 0);
4123		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4124			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MAX_PLUS_1);
4125		} else {
4126			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MIN_MINUS_1);
4127		}
4128		if (opline->result_type != IS_UNUSED) {
4129			old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
4130			if (opline->opcode == ZEND_PRE_INC) {
4131				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
4132				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX_PLUS_1);
4133			} else if (opline->opcode == ZEND_PRE_DEC) {
4134				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
4135				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN_MINUS_1);
4136			} else if (opline->opcode == ZEND_POST_INC) {
4137				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
4138				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX);
4139			} else if (opline->opcode == ZEND_POST_DEC) {
4140				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
4141				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN);
4142			}
4143		}
4144
4145		exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
4146		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
4147		if (!exit_addr) {
4148			return 0;
4149		}
4150		|	jo &exit_addr
4151
4152		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4153		    opline->result_type != IS_UNUSED) {
4154			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4155		}
4156
4157		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
4158		if (opline->result_type != IS_UNUSED) {
4159			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
4160		}
4161	} else if (may_overflow) {
4162		|	jo >1
4163		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4164		    opline->result_type != IS_UNUSED) {
4165			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4166		}
4167		|.cold_code
4168		|1:
4169		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4170			|.if X64
4171				|	mov64 rax, 0x43e0000000000000
4172				|	SET_ZVAL_LVAL op1_def_addr, rax
4173			|.else
4174				|	SET_ZVAL_LVAL op1_def_addr, 0
4175				|	SET_ZVAL_W2 op1_def_addr, 0x41e00000
4176			|.endif
4177		} else {
4178			|.if X64
4179				|	mov64 rax, 0xc3e0000000000000
4180				|	SET_ZVAL_LVAL op1_def_addr, rax
4181			|.else
4182				|	SET_ZVAL_LVAL op1_def_addr, 0x00200000
4183				|	SET_ZVAL_W2 op1_def_addr, 0xc1e00000
4184			|.endif
4185		}
4186		if (Z_MODE(op1_def_addr) == IS_MEM_ZVAL) {
4187			|	SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE
4188		}
4189		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4190		    opline->result_type != IS_UNUSED) {
4191			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_DOUBLE, ZREG_R0, ZREG_R1
4192		}
4193		|	jmp >3
4194		|.code
4195	} else {
4196		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4197		    opline->result_type != IS_UNUSED) {
4198			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4199		}
4200	}
4201	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
4202		|.cold_code
4203		|2:
4204		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4205			|	SET_EX_OPLINE opline, r0
4206			if (op1_info & MAY_BE_UNDEF) {
4207				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >2
4208				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
4209				|	mov FCARG1d, opline->op1.var
4210				|	EXT_CALL zend_jit_undefined_op_helper, r0
4211				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL
4212				op1_info |= MAY_BE_NULL;
4213			}
4214			|2:
4215			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
4216
4217			|	// ZVAL_DEREF(var_ptr);
4218			if (op1_info & MAY_BE_REF) {
4219				|	IF_NOT_Z_TYPE, FCARG1a, IS_REFERENCE, >2
4220				|	GET_Z_PTR FCARG1a, FCARG1a
4221				|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
4222				|	jz >1
4223				if (RETURN_VALUE_USED(opline)) {
4224					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
4225				} else {
4226					|	xor FCARG2a, FCARG2a
4227				}
4228				if (opline->opcode == ZEND_PRE_INC) {
4229					|	EXT_CALL zend_jit_pre_inc_typed_ref, r0
4230				} else if (opline->opcode == ZEND_PRE_DEC) {
4231					|	EXT_CALL zend_jit_pre_dec_typed_ref, r0
4232				} else if (opline->opcode == ZEND_POST_INC) {
4233					|	EXT_CALL zend_jit_post_inc_typed_ref, r0
4234				} else if (opline->opcode == ZEND_POST_DEC) {
4235					|	EXT_CALL zend_jit_post_dec_typed_ref, r0
4236				} else {
4237					ZEND_UNREACHABLE();
4238				}
4239				zend_jit_check_exception(Dst);
4240				|	jmp >3
4241				|1:
4242				|	lea FCARG1a, [FCARG1a + offsetof(zend_reference, val)]
4243				|2:
4244			}
4245
4246			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
4247				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
4248
4249				|	ZVAL_COPY_VALUE res_addr, res_use_info, val_addr, op1_info, ZREG_R0, ZREG_R2
4250				|	TRY_ADDREF op1_info, ah, r2
4251			}
4252			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4253				if (opline->opcode == ZEND_PRE_INC && opline->result_type != IS_UNUSED) {
4254					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
4255					|	EXT_CALL zend_jit_pre_inc, r0
4256				} else {
4257					|	EXT_CALL increment_function, r0
4258				}
4259			} else {
4260				if (opline->opcode == ZEND_PRE_DEC && opline->result_type != IS_UNUSED) {
4261					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
4262					|	EXT_CALL zend_jit_pre_dec, r0
4263				} else {
4264					|	EXT_CALL decrement_function, r0
4265				}
4266			}
4267			if (may_throw) {
4268				zend_jit_check_exception(Dst);
4269			}
4270		} else {
4271			zend_reg tmp_reg;
4272
4273			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
4274				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_DOUBLE, ZREG_R0, ZREG_R2
4275			}
4276			if (Z_MODE(op1_def_addr) == IS_REG) {
4277				tmp_reg = Z_REG(op1_def_addr);
4278			} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4279				tmp_reg = Z_REG(op1_addr);
4280			} else {
4281				tmp_reg = ZREG_XMM0;
4282			}
4283			|	DOUBLE_GET_ZVAL_DVAL tmp_reg, op1_addr
4284			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4285				if (CAN_USE_AVX()) {
4286					|	vaddsd xmm(tmp_reg-ZREG_XMM0), xmm(tmp_reg-ZREG_XMM0), qword [->one]
4287				} else {
4288					|	addsd xmm(tmp_reg-ZREG_XMM0), qword [->one]
4289				}
4290			} else {
4291				if (CAN_USE_AVX()) {
4292					|	vsubsd xmm(tmp_reg-ZREG_XMM0), xmm(tmp_reg-ZREG_XMM0), qword [->one]
4293				} else {
4294					|	subsd xmm(tmp_reg-ZREG_XMM0), qword [->one]
4295				}
4296			}
4297			|	DOUBLE_SET_ZVAL_DVAL op1_def_addr, tmp_reg
4298			if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4299			    opline->result_type != IS_UNUSED) {
4300				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, op1_def_info, ZREG_R0, ZREG_R1
4301				|	TRY_ADDREF op1_def_info, ah, r1
4302			}
4303		}
4304		|	jmp >3
4305		|.code
4306	}
4307	|3:
4308	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_def_addr, op1_def_info, op1_addr, op1_info)) {
4309		return 0;
4310	}
4311	if (opline->result_type != IS_UNUSED) {
4312		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
4313			return 0;
4314		}
4315	}
4316	return 1;
4317}
4318
4319static int zend_jit_opline_uses_reg(const zend_op  *opline, int8_t reg)
4320{
4321	if ((opline+1)->opcode == ZEND_OP_DATA
4322	 && ((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV))
4323	 && JIT_G(current_frame)->stack[EX_VAR_TO_NUM((opline+1)->op1.var)].reg == reg) {
4324		return 1;
4325	}
4326	return
4327		((opline->result_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4328			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->result.var)].reg == reg) ||
4329		((opline->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4330			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op1.var)].reg == reg) ||
4331		((opline->op2_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4332			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op2.var)].reg == reg);
4333}
4334
4335static int zend_jit_math_long_long(dasm_State    **Dst,
4336                                   const zend_op  *opline,
4337                                   uint8_t      opcode,
4338                                   zend_jit_addr   op1_addr,
4339                                   zend_jit_addr   op2_addr,
4340                                   zend_jit_addr   res_addr,
4341                                   uint32_t        res_info,
4342                                   uint32_t        res_use_info,
4343                                   int             may_overflow)
4344{
4345	bool must_set_cflags = 0;
4346	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4347	zend_reg result_reg;
4348	zend_reg tmp_reg = ZREG_R0;
4349
4350	if (Z_MODE(res_addr) == IS_REG && (res_info & MAY_BE_LONG)) {
4351		if (may_overflow && (res_info & MAY_BE_GUARD)
4352		 && JIT_G(current_frame)
4353		 && zend_jit_opline_uses_reg(opline, Z_REG(res_addr))) {
4354			result_reg = ZREG_R0;
4355		} else {
4356			result_reg = Z_REG(res_addr);
4357		}
4358	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr) && !may_overflow) {
4359		result_reg = Z_REG(op1_addr);
4360	} else if (Z_REG(res_addr) != ZREG_R0) {
4361		result_reg = ZREG_R0;
4362	} else {
4363		/* ASSIGN_DIM_OP */
4364		result_reg = ZREG_FCARG1;
4365		tmp_reg = ZREG_FCARG1;
4366	}
4367
4368	if (may_overflow) {
4369		must_set_cflags = 1;
4370	} else {
4371		const zend_op *next_opline = opline + 1;
4372
4373		if (next_opline->opcode == ZEND_IS_EQUAL ||
4374				next_opline->opcode == ZEND_IS_NOT_EQUAL ||
4375				next_opline->opcode == ZEND_IS_SMALLER ||
4376				next_opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
4377				next_opline->opcode == ZEND_CASE ||
4378				next_opline->opcode == ZEND_IS_IDENTICAL ||
4379				next_opline->opcode == ZEND_IS_NOT_IDENTICAL ||
4380				next_opline->opcode == ZEND_CASE_STRICT) {
4381			if (next_opline->op1_type == IS_CONST
4382			 && Z_TYPE_P(RT_CONSTANT(next_opline, next_opline->op1)) == IS_LONG
4383			 && Z_LVAL_P(RT_CONSTANT(next_opline, next_opline->op1)) == 0
4384			 && next_opline->op2_type == opline->result_type
4385			 && next_opline->op2.var == opline->result.var) {
4386				must_set_cflags = 1;
4387			} else if (next_opline->op2_type == IS_CONST
4388			 && Z_TYPE_P(RT_CONSTANT(next_opline, next_opline->op2)) == IS_LONG
4389			 && Z_LVAL_P(RT_CONSTANT(next_opline, next_opline->op2)) == 0
4390			 && next_opline->op2_type == opline->result_type
4391			 && next_opline->op2.var == opline->result.var) {
4392				must_set_cflags = 1;
4393			}
4394		}
4395	}
4396
4397	if (opcode == ZEND_MUL &&
4398			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4399			Z_LVAL_P(Z_ZV(op2_addr)) == 2) {
4400		if (Z_MODE(op1_addr) == IS_REG && !must_set_cflags) {
4401			|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))+Ra(Z_REG(op1_addr))]
4402		} else {
4403			|	GET_ZVAL_LVAL result_reg, op1_addr
4404			|	add Ra(result_reg), Ra(result_reg)
4405		}
4406	} else if (opcode == ZEND_MUL &&
4407			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4408			!must_set_cflags &&
4409			Z_LVAL_P(Z_ZV(op2_addr)) > 0 &&
4410			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr)))) {
4411		|	GET_ZVAL_LVAL result_reg, op1_addr
4412		|	shl Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
4413	} else if (opcode == ZEND_MUL &&
4414			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4415			Z_LVAL_P(Z_ZV(op1_addr)) == 2) {
4416		if (Z_MODE(op2_addr) == IS_REG && !must_set_cflags) {
4417			|	lea Ra(result_reg), [Ra(Z_REG(op2_addr))+Ra(Z_REG(op2_addr))]
4418		} else {
4419			|	GET_ZVAL_LVAL result_reg, op2_addr
4420			|	add Ra(result_reg), Ra(result_reg)
4421		}
4422	} else if (opcode == ZEND_MUL &&
4423			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4424			!must_set_cflags &&
4425			Z_LVAL_P(Z_ZV(op1_addr)) > 0 &&
4426			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op1_addr)))) {
4427		|	GET_ZVAL_LVAL result_reg, op2_addr
4428		|	shl Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
4429	} else if (opcode == ZEND_DIV &&
4430			(Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4431			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr))))) {
4432		|	GET_ZVAL_LVAL result_reg, op1_addr
4433		|	shr Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
4434	} else if (opcode == ZEND_ADD &&
4435			!must_set_cflags &&
4436			Z_MODE(op1_addr) == IS_REG &&
4437			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4438			IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(op2_addr)))) {
4439		|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))+Z_LVAL_P(Z_ZV(op2_addr))]
4440	} else if (opcode == ZEND_ADD &&
4441			!must_set_cflags &&
4442			Z_MODE(op2_addr) == IS_REG &&
4443			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4444			IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(op1_addr)))) {
4445		|	lea Ra(result_reg), [Ra(Z_REG(op2_addr))+Z_LVAL_P(Z_ZV(op1_addr))]
4446	} else if (opcode == ZEND_SUB &&
4447			!must_set_cflags &&
4448			Z_MODE(op1_addr) == IS_REG &&
4449			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4450			IS_SIGNED_32BIT(-Z_LVAL_P(Z_ZV(op2_addr)))) {
4451		|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))-Z_LVAL_P(Z_ZV(op2_addr))]
4452	} else {
4453		|	GET_ZVAL_LVAL result_reg, op1_addr
4454		if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4455		 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4456		 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4457			/* +/- 0 */
4458			may_overflow = 0;
4459		} else if (same_ops && opcode != ZEND_DIV) {
4460			|	LONG_MATH_REG opcode, Ra(result_reg), Ra(result_reg)
4461		} else {
4462			zend_reg tmp_reg;
4463
4464			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4465				if (Z_REG(res_addr) != ZREG_R0 && result_reg != ZREG_R0) {
4466					tmp_reg = ZREG_R0;
4467				} else if (Z_REG(res_addr) != ZREG_R1 && result_reg != ZREG_R1) {
4468					tmp_reg = ZREG_R1;
4469				} else {
4470					tmp_reg = ZREG_R2;
4471				}
4472			} else if (result_reg != ZREG_R0) {
4473				tmp_reg = ZREG_R0;
4474			} else {
4475				tmp_reg = ZREG_R1;
4476			}
4477			|	LONG_MATH opcode, result_reg, op2_addr, tmp_reg
4478			(void)tmp_reg;
4479		}
4480	}
4481	if (may_overflow) {
4482		if (res_info & MAY_BE_GUARD) {
4483			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
4484			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
4485			if (!exit_addr) {
4486				return 0;
4487			}
4488			if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) {
4489				|	jo &exit_addr
4490				if (Z_MODE(res_addr) == IS_REG && result_reg != Z_REG(res_addr)) {
4491					|	mov Ra(Z_REG(res_addr)), Ra(result_reg)
4492				}
4493			} else if ((res_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
4494				|	jno &exit_addr
4495			} else {
4496				ZEND_UNREACHABLE();
4497			}
4498		} else {
4499			if (res_info & MAY_BE_LONG) {
4500				|	jo >1
4501			} else {
4502				|	jno >1
4503			}
4504		}
4505	}
4506
4507	if (Z_MODE(res_addr) == IS_MEM_ZVAL && (res_info & MAY_BE_LONG)) {
4508		|	SET_ZVAL_LVAL res_addr, Ra(result_reg)
4509		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4510			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
4511				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
4512			}
4513		}
4514	}
4515
4516	if (may_overflow && (!(res_info & MAY_BE_GUARD) || (res_info & MAY_BE_ANY) == MAY_BE_DOUBLE)) {
4517		zend_reg tmp_reg1 = ZREG_XMM0;
4518		zend_reg tmp_reg2 = ZREG_XMM1;
4519
4520		if (res_info & MAY_BE_LONG) {
4521			|.cold_code
4522			|1:
4523		}
4524
4525		do {
4526			if ((sizeof(void*) == 8 || Z_MODE(res_addr) != IS_REG) &&
4527			    ((Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 1) ||
4528			     (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 1))) {
4529				if (opcode == ZEND_ADD) {
4530					|.if X64
4531						|	mov64 Ra(tmp_reg), 0x43e0000000000000
4532						if (Z_MODE(res_addr) == IS_REG) {
4533							|	movd xmm(Z_REG(res_addr)-ZREG_XMM0), Ra(tmp_reg)
4534						} else {
4535							|	SET_ZVAL_LVAL res_addr, Ra(tmp_reg)
4536						}
4537					|.else
4538						|	SET_ZVAL_LVAL res_addr, 0
4539						|	SET_ZVAL_W2 res_addr, 0x41e00000
4540					|.endif
4541					break;
4542				} else if (opcode == ZEND_SUB) {
4543					|.if X64
4544						|	mov64 Ra(tmp_reg), 0xc3e0000000000000
4545						if (Z_MODE(res_addr) == IS_REG) {
4546							|	movd xmm(Z_REG(res_addr)-ZREG_XMM0), Ra(tmp_reg)
4547						} else {
4548							|	SET_ZVAL_LVAL res_addr, Ra(tmp_reg)
4549						}
4550					|.else
4551						|	SET_ZVAL_LVAL res_addr, 0x00200000
4552						|	SET_ZVAL_W2 res_addr, 0xc1e00000
4553					|.endif
4554					break;
4555				}
4556			}
4557
4558			|	DOUBLE_GET_ZVAL_LVAL tmp_reg1, op1_addr, tmp_reg
4559			|	DOUBLE_GET_ZVAL_LVAL tmp_reg2, op2_addr, tmp_reg
4560			if (CAN_USE_AVX()) {
4561				|	AVX_MATH_REG opcode, tmp_reg1, tmp_reg1, tmp_reg2
4562			} else {
4563				|	SSE_MATH_REG opcode, tmp_reg1, tmp_reg2
4564			}
4565			|	DOUBLE_SET_ZVAL_DVAL res_addr, tmp_reg1
4566		} while (0);
4567
4568		if (Z_MODE(res_addr) == IS_MEM_ZVAL
4569		 && (res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4570			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4571		}
4572		if (res_info & MAY_BE_LONG) {
4573			|	jmp >2
4574			|.code
4575		}
4576		|2:
4577	}
4578
4579	return 1;
4580}
4581
4582static int zend_jit_math_long_double(dasm_State    **Dst,
4583                                     uint8_t      opcode,
4584                                     zend_jit_addr   op1_addr,
4585                                     zend_jit_addr   op2_addr,
4586                                     zend_jit_addr   res_addr,
4587                                     uint32_t        res_use_info)
4588{
4589	zend_reg result_reg =
4590		(Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : ZREG_XMM0;
4591	zend_reg tmp_reg;
4592
4593	if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4594		/* ASSIGN_DIM_OP */
4595		tmp_reg = ZREG_R1;
4596	} else {
4597		tmp_reg = ZREG_R0;
4598	}
4599
4600	|	DOUBLE_GET_ZVAL_LVAL result_reg, op1_addr, tmp_reg
4601
4602	if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4603		/* ASSIGN_DIM_OP */
4604		if (CAN_USE_AVX()) {
4605			|	AVX_MATH opcode, result_reg, result_reg, op2_addr, r1
4606		} else {
4607			|	SSE_MATH opcode, result_reg, op2_addr, r1
4608		}
4609	} else {
4610		if (CAN_USE_AVX()) {
4611			|	AVX_MATH opcode, result_reg, result_reg, op2_addr, r0
4612		} else {
4613			|	SSE_MATH opcode, result_reg, op2_addr, r0
4614		}
4615	}
4616	|	DOUBLE_SET_ZVAL_DVAL res_addr, result_reg
4617
4618	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4619		if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4620			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4621		}
4622	}
4623
4624	return 1;
4625}
4626
4627static int zend_jit_math_double_long(dasm_State    **Dst,
4628                                     uint8_t      opcode,
4629                                     zend_jit_addr   op1_addr,
4630                                     zend_jit_addr   op2_addr,
4631                                     zend_jit_addr   res_addr,
4632                                     uint32_t        res_use_info)
4633{
4634	zend_reg result_reg, tmp_reg_gp;
4635
4636	if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4637		/* ASSIGN_DIM_OP */
4638		tmp_reg_gp = ZREG_R1;
4639	} else {
4640		tmp_reg_gp = ZREG_R0;
4641	}
4642
4643	if (zend_is_commutative(opcode)
4644	 && (Z_MODE(res_addr) != IS_REG || Z_MODE(op1_addr) != IS_REG || Z_REG(res_addr) != Z_REG(op1_addr))) {
4645		if (Z_MODE(res_addr) == IS_REG) {
4646			result_reg = Z_REG(res_addr);
4647		} else {
4648			result_reg = ZREG_XMM0;
4649		}
4650		|	DOUBLE_GET_ZVAL_LVAL result_reg, op2_addr, tmp_reg_gp
4651		if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4652			/* ASSIGN_DIM_OP */
4653			if (CAN_USE_AVX()) {
4654				|	AVX_MATH opcode, result_reg, result_reg, op1_addr, r1
4655			} else {
4656				|	SSE_MATH opcode, result_reg, op1_addr, r1
4657			}
4658		} else {
4659			if (CAN_USE_AVX()) {
4660				|	AVX_MATH opcode, result_reg, result_reg, op1_addr, r0
4661			} else {
4662				|	SSE_MATH opcode, result_reg, op1_addr, r0
4663			}
4664		}
4665	} else {
4666		zend_reg tmp_reg;
4667
4668		if (Z_MODE(res_addr) == IS_REG) {
4669			result_reg = Z_REG(res_addr);
4670			tmp_reg = (result_reg == ZREG_XMM0) ? ZREG_XMM1 : ZREG_XMM0;
4671		} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4672			result_reg = Z_REG(op1_addr);
4673			tmp_reg = ZREG_XMM0;
4674		} else {
4675			result_reg = ZREG_XMM0;
4676			tmp_reg = ZREG_XMM1;
4677		}
4678		if (CAN_USE_AVX()) {
4679			zend_reg op1_reg;
4680
4681			if (Z_MODE(op1_addr) == IS_REG) {
4682				op1_reg = Z_REG(op1_addr);
4683			} else {
4684				|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4685				op1_reg = result_reg;
4686			}
4687			if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4688			 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4689			 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4690				/* +/- 0 */
4691			} else {
4692				|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, tmp_reg_gp
4693				|	AVX_MATH_REG opcode, result_reg, op1_reg, tmp_reg
4694			}
4695		} else {
4696			|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4697			if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4698			 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4699			 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4700				/* +/- 0 */
4701			} else {
4702				|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, tmp_reg_gp
4703				|	SSE_MATH_REG opcode, result_reg, tmp_reg
4704			}
4705		}
4706	}
4707	|	DOUBLE_SET_ZVAL_DVAL res_addr, result_reg
4708
4709	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4710		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4711			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4712				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4713			}
4714		}
4715	}
4716
4717	return 1;
4718}
4719
4720static int zend_jit_math_double_double(dasm_State    **Dst,
4721                                       uint8_t      opcode,
4722                                       zend_jit_addr   op1_addr,
4723                                       zend_jit_addr   op2_addr,
4724                                       zend_jit_addr   res_addr,
4725                                       uint32_t        res_use_info)
4726{
4727	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4728	zend_reg result_reg;
4729
4730	if (Z_MODE(res_addr) == IS_REG) {
4731		result_reg = Z_REG(res_addr);
4732	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4733		result_reg = Z_REG(op1_addr);
4734	} else if (zend_is_commutative(opcode) && Z_MODE(op2_addr) == IS_REG && Z_LAST_USE(op2_addr)) {
4735		result_reg = Z_REG(op2_addr);
4736	} else {
4737		result_reg = ZREG_XMM0;
4738	}
4739
4740	if (CAN_USE_AVX()) {
4741		zend_reg op1_reg;
4742		zend_jit_addr val_addr;
4743
4744		if (Z_MODE(op1_addr) == IS_REG) {
4745			op1_reg = Z_REG(op1_addr);
4746			val_addr = op2_addr;
4747		} else if (Z_MODE(op2_addr) == IS_REG && zend_is_commutative(opcode)) {
4748			op1_reg = Z_REG(op2_addr);
4749			val_addr = op1_addr;
4750		} else {
4751			|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4752			op1_reg = result_reg;
4753			val_addr = op2_addr;
4754		}
4755		if ((opcode == ZEND_MUL) &&
4756			Z_MODE(val_addr) == IS_CONST_ZVAL && Z_DVAL_P(Z_ZV(val_addr)) == 2.0) {
4757			|	AVX_MATH_REG ZEND_ADD, result_reg, op1_reg, op1_reg
4758		} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4759			/* ASSIGN_DIM_OP */
4760			|	AVX_MATH opcode, result_reg, op1_reg, val_addr, r1
4761		} else {
4762			|	AVX_MATH opcode, result_reg, op1_reg, val_addr, r0
4763		}
4764	} else {
4765		zend_jit_addr val_addr;
4766
4767		if (Z_MODE(op1_addr) != IS_REG && Z_MODE(op2_addr) == IS_REG && zend_is_commutative(opcode)) {
4768			|	DOUBLE_GET_ZVAL_DVAL result_reg, op2_addr
4769			val_addr = op1_addr;
4770		} else {
4771			|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4772			val_addr = op2_addr;
4773		}
4774		if (same_ops) {
4775			|	SSE_MATH_REG opcode, result_reg, result_reg
4776		} else if ((opcode == ZEND_MUL) &&
4777			Z_MODE(val_addr) == IS_CONST_ZVAL && Z_DVAL_P(Z_ZV(val_addr)) == 2.0) {
4778			|	SSE_MATH_REG ZEND_ADD, result_reg, result_reg
4779		} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4780			/* ASSIGN_DIM_OP */
4781			|	SSE_MATH opcode, result_reg, val_addr, r1
4782		} else {
4783			|	SSE_MATH opcode, result_reg, val_addr, r0
4784		}
4785	}
4786	|	DOUBLE_SET_ZVAL_DVAL res_addr, result_reg
4787
4788	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4789		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4790			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4791				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4792			}
4793		}
4794	}
4795
4796	return 1;
4797}
4798
4799static int zend_jit_math_helper(dasm_State    **Dst,
4800                                const zend_op  *opline,
4801                                uint8_t      opcode,
4802                                uint8_t      op1_type,
4803                                znode_op        op1,
4804                                zend_jit_addr   op1_addr,
4805                                uint32_t        op1_info,
4806                                uint8_t      op2_type,
4807                                znode_op        op2,
4808                                zend_jit_addr   op2_addr,
4809                                uint32_t        op2_info,
4810                                uint32_t        res_var,
4811                                zend_jit_addr   res_addr,
4812                                uint32_t        res_info,
4813                                uint32_t        res_use_info,
4814                                int             may_overflow,
4815                                int             may_throw)
4816/* Labels: 1,2,3,4,5,6 */
4817{
4818	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4819
4820	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
4821		if (op1_info & (MAY_BE_ANY-MAY_BE_LONG)) {
4822			if (op1_info & MAY_BE_DOUBLE) {
4823				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
4824			} else {
4825				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
4826			}
4827		}
4828		if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_LONG))) {
4829			if (op2_info & MAY_BE_DOUBLE) {
4830				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >1
4831				|.cold_code
4832				|1:
4833				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4834					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6
4835				}
4836				if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4837					return 0;
4838				}
4839				|	jmp >5
4840				|.code
4841			} else {
4842				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
4843			}
4844		}
4845		if (!zend_jit_math_long_long(Dst, opline, opcode, op1_addr, op2_addr, res_addr, res_info, res_use_info, may_overflow)) {
4846			return 0;
4847		}
4848		if (op1_info & MAY_BE_DOUBLE) {
4849			|.cold_code
4850			|3:
4851			if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4852				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6
4853			}
4854			if (op2_info & MAY_BE_DOUBLE) {
4855				if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4856					if (!same_ops) {
4857						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >1
4858					} else {
4859						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >6
4860					}
4861				}
4862				if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4863					return 0;
4864				}
4865				|	jmp >5
4866			}
4867			if (!same_ops) {
4868				|1:
4869				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4870					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
4871				}
4872				if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4873					return 0;
4874				}
4875				|	jmp >5
4876			}
4877			|.code
4878		}
4879	} else if ((op1_info & MAY_BE_DOUBLE) &&
4880	           !(op1_info & MAY_BE_LONG) &&
4881	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4882	           (res_info & MAY_BE_DOUBLE)) {
4883		if (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
4884			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6
4885		}
4886		if (op2_info & MAY_BE_DOUBLE) {
4887			if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4888				if (!same_ops && (op2_info & MAY_BE_LONG)) {
4889					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >1
4890				} else {
4891					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6
4892				}
4893			}
4894			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4895				return 0;
4896			}
4897		}
4898		if (!same_ops && (op2_info & MAY_BE_LONG)) {
4899			if (op2_info & MAY_BE_DOUBLE) {
4900				|.cold_code
4901			}
4902		    |1:
4903			if (op2_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
4904				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
4905			}
4906			if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4907				return 0;
4908			}
4909			if (op2_info & MAY_BE_DOUBLE) {
4910				|	jmp >5
4911				|.code
4912			}
4913		}
4914	} else if ((op2_info & MAY_BE_DOUBLE) &&
4915	           !(op2_info & MAY_BE_LONG) &&
4916	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4917	           (res_info & MAY_BE_DOUBLE)) {
4918		if (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
4919			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6
4920		}
4921		if (op1_info & MAY_BE_DOUBLE) {
4922			if (!same_ops && (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4923				if (!same_ops && (op1_info & MAY_BE_LONG)) {
4924					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >1
4925				} else {
4926					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6
4927				}
4928			}
4929			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4930				return 0;
4931			}
4932		}
4933		if (!same_ops && (op1_info & MAY_BE_LONG)) {
4934			if (op1_info & MAY_BE_DOUBLE) {
4935				|.cold_code
4936			}
4937			|1:
4938			if (op1_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
4939				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
4940			}
4941			if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4942				return 0;
4943			}
4944			if (op1_info & MAY_BE_DOUBLE) {
4945				|	jmp >5
4946				|.code
4947			}
4948		}
4949	}
4950
4951	|5:
4952
4953	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
4954		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
4955		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4956		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4957		    (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
4958			|.cold_code
4959		}
4960		|6:
4961		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
4962			if (Z_MODE(res_addr) == IS_REG) {
4963				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4964				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
4965			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
4966				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
4967			}
4968			if (Z_MODE(op1_addr) == IS_REG) {
4969				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
4970				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
4971					return 0;
4972				}
4973				op1_addr = real_addr;
4974			}
4975			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
4976		} else {
4977			if (Z_MODE(op1_addr) == IS_REG) {
4978				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
4979				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
4980					return 0;
4981				}
4982				op1_addr = real_addr;
4983			}
4984			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
4985			if (Z_MODE(res_addr) == IS_REG) {
4986				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4987				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
4988			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
4989				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
4990			}
4991		}
4992
4993		if (Z_MODE(op2_addr) == IS_REG) {
4994			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
4995			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
4996				return 0;
4997			}
4998			op2_addr = real_addr;
4999		}
5000		|.if X64
5001			|	LOAD_ZVAL_ADDR CARG3, op2_addr
5002		|.else
5003			|	sub r4, 12
5004			|	PUSH_ZVAL_ADDR op2_addr, r0
5005		|.endif
5006		|	SET_EX_OPLINE opline, r0
5007		if (opcode == ZEND_ADD) {
5008			|	EXT_CALL add_function, r0
5009		} else if (opcode == ZEND_SUB) {
5010			|	EXT_CALL sub_function, r0
5011		} else if (opcode == ZEND_MUL) {
5012			|	EXT_CALL mul_function, r0
5013		} else if (opcode == ZEND_DIV) {
5014			|	EXT_CALL div_function, r0
5015		} else {
5016			ZEND_UNREACHABLE();
5017		}
5018		|.if not(X64)
5019		|	add r4, 12
5020		|.endif
5021		|	FREE_OP op1_type, op1, op1_info, 0, NULL
5022		|	FREE_OP op2_type, op2, op2_info, 0, NULL
5023		if (may_throw) {
5024			if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
5025				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
5026				|	jne ->exception_handler_free_op2
5027			} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
5028				zend_jit_check_exception_undef_result(Dst, opline);
5029			} else {
5030				zend_jit_check_exception(Dst);
5031			}
5032		}
5033		if (Z_MODE(res_addr) == IS_REG) {
5034			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5035			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
5036				return 0;
5037			}
5038		}
5039		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
5040		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
5041		    (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
5042			|	jmp <5
5043			|.code
5044		}
5045	}
5046
5047	return 1;
5048}
5049
5050static 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)
5051{
5052	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
5053	ZEND_ASSERT((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
5054	    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)));
5055
5056	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)) {
5057		return 0;
5058	}
5059	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
5060		return 0;
5061	}
5062	return 1;
5063}
5064
5065static int zend_jit_add_arrays(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, zend_jit_addr res_addr)
5066{
5067	if (Z_MODE(op2_addr) != IS_MEM_ZVAL || Z_REG(op2_addr) != ZREG_FCARG1) {
5068		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
5069		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5070	} else if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG2) {
5071		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5072		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
5073	} else {
5074		|	GET_ZVAL_LVAL ZREG_R0, op2_addr
5075		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
5076		|	mov FCARG2a, r0
5077	}
5078	|	EXT_CALL zend_jit_add_arrays_helper, r0
5079	|	SET_ZVAL_PTR res_addr, r0
5080	|	SET_ZVAL_TYPE_INFO res_addr, IS_ARRAY_EX
5081	|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
5082	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
5083	return 1;
5084}
5085
5086static int zend_jit_long_math_helper(dasm_State    **Dst,
5087                                     const zend_op  *opline,
5088                                     uint8_t      opcode,
5089                                     uint8_t      op1_type,
5090                                     znode_op        op1,
5091                                     zend_jit_addr   op1_addr,
5092                                     uint32_t        op1_info,
5093                                     zend_ssa_range *op1_range,
5094                                     uint8_t      op2_type,
5095                                     znode_op        op2,
5096                                     zend_jit_addr   op2_addr,
5097                                     uint32_t        op2_info,
5098                                     zend_ssa_range *op2_range,
5099                                     uint32_t        res_var,
5100                                     zend_jit_addr   res_addr,
5101                                     uint32_t        res_info,
5102                                     uint32_t        res_use_info,
5103                                     int             may_throw)
5104/* Labels: 6 */
5105{
5106	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
5107	zend_reg result_reg;
5108
5109	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
5110		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
5111	}
5112	if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
5113		|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
5114	}
5115
5116	if (opcode == ZEND_MOD) {
5117		result_reg = ZREG_RAX;
5118	} else if (Z_MODE(res_addr) == IS_REG) {
5119		if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR)
5120		 && opline->op2_type != IS_CONST) {
5121			result_reg = ZREG_R0;
5122		} else {
5123			result_reg = Z_REG(res_addr);
5124		}
5125	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
5126		result_reg = Z_REG(op1_addr);
5127	} else if (Z_REG(res_addr) != ZREG_R0) {
5128		result_reg = ZREG_R0;
5129	} else {
5130		/* ASSIGN_DIM_OP */
5131		if (ZREG_FCARG1 == ZREG_RCX
5132		 && (opcode == ZEND_SL || opcode == ZEND_SR)
5133		 && Z_MODE(op2_addr) != IS_CONST_ZVAL) {
5134			result_reg = ZREG_R2;
5135		} else {
5136			result_reg = ZREG_FCARG1;
5137		}
5138	}
5139
5140	if (opcode == ZEND_SL) {
5141		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5142			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
5143
5144			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
5145				if (EXPECTED(op2_lval > 0)) {
5146					|	xor Ra(result_reg), Ra(result_reg)
5147				} else {
5148					zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5149					zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5150					|	SET_EX_OPLINE opline, r0
5151					|	jmp ->negative_shift
5152				}
5153			} else if (Z_MODE(op1_addr) == IS_REG && op2_lval == 1) {
5154				|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))+Ra(Z_REG(op1_addr))]
5155			} else {
5156				|	GET_ZVAL_LVAL result_reg, op1_addr
5157				|	shl Ra(result_reg), op2_lval
5158			}
5159		} else {
5160			if (Z_MODE(op2_addr) != IS_REG || Z_REG(op2_addr) != ZREG_RCX) {
5161				|	GET_ZVAL_LVAL ZREG_RCX, op2_addr
5162			}
5163			if (!op2_range ||
5164			     op2_range->min < 0 ||
5165			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
5166				|	cmp r1, (SIZEOF_ZEND_LONG*8)
5167				|	jae >1
5168				|.cold_code
5169				|1:
5170				|	cmp r1, 0
5171				|	mov Ra(result_reg), 0
5172				|	jg >1
5173				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5174				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5175				|	SET_EX_OPLINE opline, r0
5176				|	jmp ->negative_shift
5177				|.code
5178			}
5179			|	GET_ZVAL_LVAL result_reg, op1_addr
5180			|	shl Ra(result_reg), cl
5181			|1:
5182		}
5183	} else if (opcode == ZEND_SR) {
5184		|	GET_ZVAL_LVAL result_reg, op1_addr
5185		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5186			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
5187
5188			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
5189				if (EXPECTED(op2_lval > 0)) {
5190					|	sar Ra(result_reg), (SIZEOF_ZEND_LONG * 8) - 1
5191				} else {
5192					zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5193					zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5194					|	SET_EX_OPLINE opline, r0
5195					|	jmp ->negative_shift
5196				}
5197			} else {
5198				|	sar Ra(result_reg), op2_lval
5199			}
5200		} else {
5201			if (Z_MODE(op2_addr) != IS_REG || Z_REG(op2_addr) != ZREG_RCX) {
5202				|	GET_ZVAL_LVAL ZREG_RCX, op2_addr
5203			}
5204			if (!op2_range ||
5205			     op2_range->min < 0 ||
5206			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
5207				|	cmp r1, (SIZEOF_ZEND_LONG*8)
5208				|	jae >1
5209				|.cold_code
5210				|1:
5211				|	cmp r1, 0
5212				|	mov r1, (SIZEOF_ZEND_LONG * 8) - 1
5213				|	jg >1
5214				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5215				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5216				|	SET_EX_OPLINE opline, r0
5217				|	jmp ->negative_shift
5218				|.code
5219			}
5220			|1:
5221			|	sar Ra(result_reg), cl
5222		}
5223	} else if (opcode == ZEND_MOD) {
5224		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5225			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
5226
5227			if (op2_lval == 0) {
5228				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5229				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5230				|	SET_EX_OPLINE opline, r0
5231				|	jmp ->mod_by_zero
5232			} else if (zend_long_is_power_of_two(op2_lval) && op1_range && op1_range->min >= 0) {
5233				zval tmp;
5234				zend_jit_addr tmp_addr;
5235				zend_reg tmp_reg;
5236
5237				/* Optimisation for mod of power of 2 */
5238				ZVAL_LONG(&tmp, op2_lval - 1);
5239				tmp_addr = ZEND_ADDR_CONST_ZVAL(&tmp);
5240				if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
5241					tmp_reg = ZREG_R1;
5242				} else if (result_reg != ZREG_R0) {
5243					tmp_reg = ZREG_R0;
5244				} else {
5245					tmp_reg = ZREG_R1;
5246				}
5247				|	GET_ZVAL_LVAL result_reg, op1_addr
5248				|	LONG_MATH ZEND_BW_AND, result_reg, tmp_addr, tmp_reg
5249				(void)tmp_reg;
5250			} else {
5251				if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5252					|	mov aword T1, r0 // save
5253				} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RCX) {
5254					|	mov aword T1, Ra(ZREG_RCX) // save
5255				}
5256				result_reg = ZREG_RDX;
5257				if (op2_lval == -1) {
5258					|	xor Ra(result_reg), Ra(result_reg)
5259				} else {
5260					|	GET_ZVAL_LVAL ZREG_RAX, op1_addr
5261					|	GET_ZVAL_LVAL ZREG_RCX, op2_addr
5262					|.if X64
5263					|	cqo
5264					|.else
5265					|	cdq
5266					|.endif
5267					|	idiv Ra(ZREG_RCX)
5268				}
5269				if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5270					|	mov r0, aword T1 // restore
5271				} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RCX) {
5272					|	mov Ra(ZREG_RCX), aword T1 // restore
5273				}
5274			}
5275		} else {
5276			if ((op2_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) || !op2_range || (op2_range->min <= 0 && op2_range->max >= 0)) {
5277				if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
5278					|	cmp aword [Ra(Z_REG(op2_addr))+Z_OFFSET(op2_addr)], 0
5279				} else if (Z_MODE(op2_addr) == IS_REG) {
5280					|	test Ra(Z_REG(op2_addr)), Ra(Z_REG(op2_addr))
5281				}
5282				|	jz >1
5283				|.cold_code
5284				|1:
5285				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5286				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5287				|	SET_EX_OPLINE opline, r0
5288				|	jmp ->mod_by_zero
5289				|.code
5290			}
5291
5292			/* Prevent overflow error/crash if op1 == LONG_MIN and op2 == -1 */
5293			if (!op2_range || (op2_range->min <= -1 && op2_range->max >= -1)) {
5294				if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
5295					|	cmp aword [Ra(Z_REG(op2_addr))+Z_OFFSET(op2_addr)], -1
5296				} else if (Z_MODE(op2_addr) == IS_REG) {
5297					|	cmp Ra(Z_REG(op2_addr)), -1
5298				}
5299				|	jz >1
5300				|.cold_code
5301				|1:
5302				|	SET_ZVAL_LVAL res_addr, 0
5303				if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
5304					if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
5305						if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
5306							|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
5307						}
5308					}
5309				}
5310				|	jmp >5
5311				|.code
5312			}
5313
5314			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5315				|	mov aword T1, r0 // save
5316			}
5317			result_reg = ZREG_RDX;
5318			|	GET_ZVAL_LVAL ZREG_RAX, op1_addr
5319			|.if X64
5320			|	cqo
5321			|.else
5322			|	cdq
5323			|.endif
5324			if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
5325				|	idiv aword [Ra(Z_REG(op2_addr))+Z_OFFSET(op2_addr)]
5326			} else if (Z_MODE(op2_addr) == IS_REG) {
5327				|	idiv Ra(Z_REG(op2_addr))
5328			}
5329			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5330				|	mov r0, aword T1 // restore
5331			}
5332		}
5333	} else if (same_ops) {
5334		|	GET_ZVAL_LVAL result_reg, op1_addr
5335		|	LONG_MATH_REG opcode, Ra(result_reg), Ra(result_reg)
5336	} else {
5337		zend_reg tmp_reg;
5338
5339		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
5340			if (Z_REG(res_addr) != ZREG_R0 && result_reg != ZREG_R0) {
5341				tmp_reg = ZREG_R0;
5342			} else if (Z_REG(res_addr) != ZREG_R1 && result_reg != ZREG_R1) {
5343				tmp_reg = ZREG_R1;
5344			} else {
5345				tmp_reg = ZREG_R2;
5346			}
5347		} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R1) {
5348			tmp_reg = ZREG_R0;
5349		} else if (result_reg != ZREG_R0) {
5350			tmp_reg = ZREG_R0;
5351		} else {
5352			tmp_reg = ZREG_R1;
5353		}
5354		|	GET_ZVAL_LVAL result_reg, op1_addr
5355		|	LONG_MATH opcode, result_reg, op2_addr, tmp_reg
5356		(void)tmp_reg;
5357	}
5358
5359	if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != result_reg) {
5360		|	SET_ZVAL_LVAL res_addr, Ra(result_reg)
5361	}
5362	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
5363		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
5364			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
5365				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
5366			}
5367		}
5368	}
5369
5370	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) ||
5371		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
5372		if ((op1_info & MAY_BE_LONG) &&
5373		    (op2_info & MAY_BE_LONG)) {
5374			|.cold_code
5375		}
5376		|6:
5377		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
5378			if (Z_MODE(res_addr) == IS_REG) {
5379				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5380				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
5381			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5382				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5383			}
5384			if (Z_MODE(op1_addr) == IS_REG) {
5385				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
5386				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
5387					return 0;
5388				}
5389				op1_addr = real_addr;
5390			}
5391			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5392		} else {
5393			if (Z_MODE(op1_addr) == IS_REG) {
5394				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
5395				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
5396					return 0;
5397				}
5398				op1_addr = real_addr;
5399			}
5400			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5401			if (Z_MODE(res_addr) == IS_REG) {
5402				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5403				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
5404			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5405				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5406			}
5407		}
5408		if (Z_MODE(op2_addr) == IS_REG) {
5409			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
5410			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
5411				return 0;
5412			}
5413			op2_addr = real_addr;
5414		}
5415		|.if X64
5416			|	LOAD_ZVAL_ADDR CARG3, op2_addr
5417		|.else
5418			|	sub r4, 12
5419			|	PUSH_ZVAL_ADDR op2_addr, r0
5420		|.endif
5421		|	SET_EX_OPLINE opline, r0
5422		if (opcode == ZEND_BW_OR) {
5423			|	EXT_CALL bitwise_or_function, r0
5424		} else if (opcode == ZEND_BW_AND) {
5425			|	EXT_CALL bitwise_and_function, r0
5426		} else if (opcode == ZEND_BW_XOR) {
5427			|	EXT_CALL bitwise_xor_function, r0
5428		} else if (opcode == ZEND_SL) {
5429			|	EXT_CALL shift_left_function, r0
5430		} else if (opcode == ZEND_SR) {
5431			|	EXT_CALL shift_right_function, r0
5432		} else if (opcode == ZEND_MOD) {
5433			|	EXT_CALL mod_function, r0
5434		} else {
5435			ZEND_UNREACHABLE();
5436		}
5437		|.if not(X64)
5438		|	add r4, 12
5439		|.endif
5440		if (op1_addr == res_addr && (op2_info & MAY_BE_RCN)) {
5441			/* compound assignment may decrement "op2" refcount */
5442			op2_info |= MAY_BE_RC1;
5443		}
5444		|	FREE_OP op1_type, op1, op1_info, 0, NULL
5445		|	FREE_OP op2_type, op2, op2_info, 0, NULL
5446		if (may_throw) {
5447			if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
5448				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
5449				|	jne ->exception_handler_free_op2
5450			} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
5451				zend_jit_check_exception_undef_result(Dst, opline);
5452			} else {
5453				zend_jit_check_exception(Dst);
5454			}
5455		}
5456		if (Z_MODE(res_addr) == IS_REG) {
5457			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5458			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
5459				return 0;
5460			}
5461		}
5462		if ((op1_info & MAY_BE_LONG) &&
5463		    (op2_info & MAY_BE_LONG)) {
5464			|	jmp >5
5465			|.code
5466		}
5467	}
5468	|5:
5469
5470	return 1;
5471}
5472
5473static 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)
5474{
5475	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
5476	ZEND_ASSERT((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG));
5477
5478	if (!zend_jit_long_math_helper(Dst, opline, opline->opcode,
5479			opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
5480			opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
5481			opline->result.var, res_addr, res_info, res_use_info, may_throw)) {
5482		return 0;
5483	}
5484	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
5485		return 0;
5486	}
5487	return 1;
5488}
5489
5490static int zend_jit_concat_helper(dasm_State    **Dst,
5491                                  const zend_op  *opline,
5492                                  uint8_t      op1_type,
5493                                  znode_op        op1,
5494                                  zend_jit_addr   op1_addr,
5495                                  uint32_t        op1_info,
5496                                  uint8_t      op2_type,
5497                                  znode_op        op2,
5498                                  zend_jit_addr   op2_addr,
5499                                  uint32_t        op2_info,
5500                                  zend_jit_addr   res_addr,
5501                                  int             may_throw)
5502{
5503#if 1
5504	if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5505		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
5506			|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6
5507		}
5508		if (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
5509			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >6
5510		}
5511		if (Z_MODE(op1_addr) == IS_MEM_ZVAL && Z_REG(op1_addr) == Z_REG(res_addr) && Z_OFFSET(op1_addr) == Z_OFFSET(res_addr)) {
5512			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5513				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5514			}
5515			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
5516			|	EXT_CALL zend_jit_fast_assign_concat_helper, r0
5517			/* concatenation with itself may reduce refcount */
5518			op2_info |= MAY_BE_RC1;
5519		} else {
5520			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5521				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5522			}
5523			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5524			|.if X64
5525				|	LOAD_ZVAL_ADDR CARG3, op2_addr
5526			|.else
5527				|	sub r4, 12
5528				|	PUSH_ZVAL_ADDR op2_addr, r0
5529			|.endif
5530			if (op1_type == IS_CV || op1_type == IS_CONST) {
5531				|	EXT_CALL zend_jit_fast_concat_helper, r0
5532			} else {
5533				|	EXT_CALL zend_jit_fast_concat_tmp_helper, r0
5534			}
5535			|.if not(X64)
5536			|	add r4, 12
5537			|.endif
5538		}
5539		/* concatenation with empty string may increase refcount */
5540		op2_info |= MAY_BE_RCN;
5541		|	FREE_OP op2_type, op2, op2_info, 0, opline
5542		|5:
5543	}
5544	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) ||
5545	    (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING))) {
5546		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5547			|.cold_code
5548			|6:
5549		}
5550#endif
5551		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
5552			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5553				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5554			}
5555			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5556		} else {
5557			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5558			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5559				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5560			}
5561		}
5562		|.if X64
5563			|	LOAD_ZVAL_ADDR CARG3, op2_addr
5564		|.else
5565			|	sub r4, 12
5566			|	PUSH_ZVAL_ADDR op2_addr, r0
5567		|.endif
5568		|	SET_EX_OPLINE opline, r0
5569		|	EXT_CALL concat_function, r0
5570		|.if not(X64)
5571		|	add r4, 12
5572		|.endif
5573		/* concatenation with empty string may increase refcount */
5574		op1_info |= MAY_BE_RCN;
5575		op2_info |= MAY_BE_RCN;
5576		|	FREE_OP op1_type, op1, op1_info, 0, NULL
5577		|	FREE_OP op2_type, op2, op2_info, 0, NULL
5578		if (may_throw) {
5579			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
5580				zend_jit_check_exception_undef_result(Dst, opline);
5581			} else {
5582				zend_jit_check_exception(Dst);
5583			}
5584		}
5585#if 1
5586		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5587			|	jmp <5
5588			|.code
5589		}
5590	}
5591#endif
5592
5593	return 1;
5594}
5595
5596static 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)
5597{
5598	zend_jit_addr op1_addr, op2_addr;
5599
5600	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
5601	ZEND_ASSERT((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING));
5602
5603	op1_addr = OP1_ADDR();
5604	op2_addr = OP2_ADDR();
5605
5606	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);
5607}
5608
5609static 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, uint8_t dim_type, const void *found_exit_addr, const void *not_found_exit_addr, const void *exit_addr)
5610/* Labels: 1,2,3,4,5 */
5611{
5612	zend_jit_addr op2_addr = OP2_ADDR();
5613	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
5614
5615	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
5616	 && type == BP_VAR_R
5617	 && !exit_addr) {
5618		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
5619		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
5620		if (!exit_addr) {
5621			return 0;
5622		}
5623	}
5624
5625	if (op2_info & MAY_BE_LONG) {
5626		bool op2_loaded = 0;
5627		bool packed_loaded = 0;
5628		bool bad_packed_key = 0;
5629
5630		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) {
5631			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_LONG))
5632			|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3
5633		}
5634		if (op1_info & MAY_BE_PACKED_GUARD) {
5635			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
5636			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
5637
5638			if (!exit_addr) {
5639				return 0;
5640			}
5641			if (op1_info & MAY_BE_ARRAY_PACKED) {
5642				|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5643				|	jz &exit_addr
5644			} else {
5645				|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5646				|	jnz &exit_addr
5647			}
5648		}
5649		if (type == BP_VAR_W) {
5650			|	// hval = Z_LVAL_P(dim);
5651			|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5652			op2_loaded = 1;
5653		}
5654		if (op1_info & MAY_BE_ARRAY_PACKED) {
5655			zend_long val = -1;
5656
5657			if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5658				val = Z_LVAL_P(Z_ZV(op2_addr));
5659				if (val >= 0 && val < HT_MAX_SIZE) {
5660					packed_loaded = 1;
5661				} else {
5662					bad_packed_key = 1;
5663				}
5664			} else {
5665				if (!op2_loaded) {
5666					|	// hval = Z_LVAL_P(dim);
5667					|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5668					op2_loaded = 1;
5669				}
5670				packed_loaded = 1;
5671			}
5672
5673			if (dim_type == IS_UNDEF && type == BP_VAR_W && packed_loaded) {
5674				/* don't generate "fast" code for packed array */
5675				packed_loaded = 0;
5676			}
5677
5678			if (packed_loaded) {
5679				|	// ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
5680				if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5681					|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5682					|	jz >4 // HASH_FIND
5683				}
5684				|	// if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed))
5685				|.if X64
5686					|	mov eax, dword [FCARG1a + offsetof(zend_array, nNumUsed)]
5687					if (val == 0) {
5688						|	test r0, r0
5689					} else if (val > 0 && !op2_loaded) {
5690						|	cmp r0, val
5691					} else {
5692						|	cmp r0, FCARG2a
5693					}
5694				|.else
5695					if (val >= 0 && !op2_loaded) {
5696						|	cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], val
5697					} else {
5698						|	cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], FCARG2a
5699					}
5700				|.endif
5701				if (type == BP_JIT_IS) {
5702					if (not_found_exit_addr) {
5703						|	jbe &not_found_exit_addr
5704					} else {
5705						|	jbe >9 // NOT_FOUND
5706					}
5707				} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5708					|	jbe &exit_addr
5709				} else if (type == BP_VAR_IS && not_found_exit_addr) {
5710					|	jbe &not_found_exit_addr
5711				} else if (type == BP_VAR_RW && not_found_exit_addr) {
5712					|	jbe &not_found_exit_addr
5713				} else if (type == BP_VAR_IS && found_exit_addr) {
5714					|	jbe >7 // NOT_FOUND
5715				} else {
5716					|	jbe >2 // NOT_FOUND
5717				}
5718				|	// _ret = &_ht->arPacked[h];
5719				if (val >= 0) {
5720					|	mov r0, aword [FCARG1a + offsetof(zend_array, arPacked)]
5721					if (val != 0) {
5722						|	add r0, val * sizeof(zval)
5723					}
5724				} else {
5725					|.if X64
5726						|	mov r0, FCARG2a
5727						|	shl r0, 4
5728					|.else
5729						|	imul r0, FCARG2a, sizeof(zval)
5730					|.endif
5731					|	add r0, aword [FCARG1a + offsetof(zend_array, arPacked)]
5732				}
5733			}
5734		}
5735		switch (type) {
5736			case BP_JIT_IS:
5737				if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5738					if (packed_loaded) {
5739						|	jmp >5
5740					}
5741					|4:
5742					if (!op2_loaded) {
5743						|	// hval = Z_LVAL_P(dim);
5744						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5745					}
5746					if (packed_loaded) {
5747						|	EXT_CALL _zend_hash_index_find, r0
5748					} else {
5749						|	EXT_CALL zend_hash_index_find, r0
5750					}
5751					|	test r0, r0
5752					if (not_found_exit_addr) {
5753						|	jz &not_found_exit_addr
5754					} else {
5755						|	jz >9 // NOT_FOUND
5756					}
5757					if (op2_info & MAY_BE_STRING) {
5758						|	jmp >5
5759					}
5760				} else if (packed_loaded) {
5761					if (op2_info & MAY_BE_STRING) {
5762						|	jmp >5
5763					}
5764				} else if (not_found_exit_addr) {
5765					|	jmp &not_found_exit_addr
5766				} else {
5767					|	jmp >9 // NOT_FOUND
5768				}
5769				break;
5770			case BP_VAR_R:
5771			case BP_VAR_IS:
5772			case BP_VAR_UNSET:
5773				if (packed_loaded) {
5774					if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5775						|	IF_NOT_Z_TYPE r0, IS_UNDEF, >8
5776					} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5777						/* perform IS_UNDEF check only after result type guard (during deoptimization) */
5778						if (!found_exit_addr || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5779							|	IF_Z_TYPE r0, IS_UNDEF, &exit_addr
5780						}
5781					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5782						|	IF_Z_TYPE r0, IS_UNDEF, &not_found_exit_addr
5783					} else if (type == BP_VAR_IS && found_exit_addr) {
5784						|	IF_Z_TYPE r0, IS_UNDEF, >7 // NOT_FOUND
5785					} else {
5786						|	IF_Z_TYPE r0, IS_UNDEF, >2 // NOT_FOUND
5787					}
5788				}
5789				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (packed_loaded && (op1_info & MAY_BE_ARRAY_NUMERIC_HASH))) {
5790					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5791						|	jmp &exit_addr
5792					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5793						|	jmp &not_found_exit_addr
5794					} else if (type == BP_VAR_IS && found_exit_addr) {
5795						|	jmp >7 // NOT_FOUND
5796					} else {
5797						|	jmp >2 // NOT_FOUND
5798					}
5799				}
5800				if (!packed_loaded || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5801					|4:
5802					if (!op2_loaded) {
5803						|	// hval = Z_LVAL_P(dim);
5804						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5805					}
5806					if (packed_loaded) {
5807						|	EXT_CALL _zend_hash_index_find, r0
5808					} else {
5809						|	EXT_CALL zend_hash_index_find, r0
5810					}
5811					|	test r0, r0
5812					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5813						|	jz &exit_addr
5814					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5815						|	jz &not_found_exit_addr
5816					} else if (type == BP_VAR_IS && found_exit_addr) {
5817						|	jz >7 // NOT_FOUND
5818					} else {
5819						|	jz >2 // NOT_FOUND
5820					}
5821				}
5822				|.cold_code
5823				|2:
5824				switch (type) {
5825					case BP_VAR_R:
5826						if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
5827							|	// zend_error(E_WARNING,"Undefined array key " ZEND_LONG_FMT, hval);
5828							|	// retval = &EG(uninitialized_zval);
5829							|	UNDEFINED_OFFSET opline
5830							|	jmp >9
5831						}
5832						break;
5833					case BP_VAR_IS:
5834					case BP_VAR_UNSET:
5835						if (!not_found_exit_addr && !found_exit_addr) {
5836							|	// retval = &EG(uninitialized_zval);
5837							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
5838							|	jmp >9
5839						}
5840						break;
5841					default:
5842						ZEND_UNREACHABLE();
5843				}
5844				|.code
5845				break;
5846			case BP_VAR_RW:
5847				if (packed_loaded && !not_found_exit_addr) {
5848					|	IF_NOT_Z_TYPE r0, IS_UNDEF, >8
5849				}
5850				if (!packed_loaded ||
5851						!not_found_exit_addr ||
5852						(op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5853					if (packed_loaded && not_found_exit_addr) {
5854						|.cold_code
5855					}
5856					|2:
5857					|4:
5858					if (!op2_loaded) {
5859						|	// hval = Z_LVAL_P(dim);
5860						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5861					}
5862					if (packed_loaded) {
5863						|	EXT_CALL zend_jit_hash_index_lookup_rw_no_packed, r0
5864					} else {
5865						|	EXT_CALL zend_jit_hash_index_lookup_rw, r0
5866					}
5867					|	test r0, r0
5868					if (not_found_exit_addr) {
5869						if (packed_loaded) {
5870							|	jnz >8
5871							|	jmp &not_found_exit_addr
5872							|.code
5873						} else {
5874							|	jz &not_found_exit_addr
5875						}
5876					} else {
5877						|	jz >9
5878					}
5879				}
5880				break;
5881			case BP_VAR_W:
5882				if (packed_loaded) {
5883					|	IF_NOT_Z_TYPE r0, IS_UNDEF, >8
5884				}
5885				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) || packed_loaded || bad_packed_key || dim_type == IS_UNDEF) {
5886					|2:
5887					|4:
5888					if (!op2_loaded) {
5889						|	// hval = Z_LVAL_P(dim);
5890						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5891					}
5892					|	EXT_CALL zend_hash_index_lookup, r0
5893				}
5894				break;
5895			default:
5896				ZEND_UNREACHABLE();
5897		}
5898
5899		if (type != BP_JIT_IS && (op2_info & MAY_BE_STRING)) {
5900			|	jmp >8
5901		}
5902	}
5903
5904	if (op2_info & MAY_BE_STRING) {
5905		|3:
5906		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
5907			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_STRING))
5908			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >3
5909		}
5910		|	// offset_key = Z_STR_P(dim);
5911		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5912		|	// retval = zend_hash_find(ht, offset_key);
5913		switch (type) {
5914			case BP_JIT_IS:
5915				if (opline->op2_type != IS_CONST) {
5916					|	cmp byte [FCARG2a + offsetof(zend_string, val)], '9'
5917					|	jle >1
5918					|.cold_code
5919					|1:
5920					|	EXT_CALL zend_jit_symtable_find, r0
5921					|	jmp >1
5922					|.code
5923					|	EXT_CALL zend_hash_find, r0
5924					|1:
5925				} else {
5926					|	EXT_CALL zend_hash_find_known_hash, r0
5927				}
5928				|	test r0, r0
5929				if (not_found_exit_addr) {
5930					|	jz &not_found_exit_addr
5931				} else {
5932					|	jz >9 // NOT_FOUND
5933				}
5934				break;
5935			case BP_VAR_R:
5936			case BP_VAR_IS:
5937			case BP_VAR_UNSET:
5938				if (opline->op2_type != IS_CONST) {
5939					|	cmp byte [FCARG2a + offsetof(zend_string, val)], '9'
5940					|	jle >1
5941					|.cold_code
5942					|1:
5943					|	EXT_CALL zend_jit_symtable_find, r0
5944					|	jmp >1
5945					|.code
5946					|	EXT_CALL zend_hash_find, r0
5947					|1:
5948				} else {
5949					|	EXT_CALL zend_hash_find_known_hash, r0
5950				}
5951				|	test r0, r0
5952				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5953					|	jz &exit_addr
5954				} else if (type == BP_VAR_IS && not_found_exit_addr) {
5955					|	jz &not_found_exit_addr
5956				} else if (type == BP_VAR_IS && found_exit_addr) {
5957					|	jz >7 // NOT_FOUND
5958				} else {
5959					|	jz >2 // NOT_FOUND
5960					|.cold_code
5961					|2:
5962					switch (type) {
5963						case BP_VAR_R:
5964							// zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
5965							|	UNDEFINED_INDEX opline
5966							|	jmp >9
5967							break;
5968						case BP_VAR_IS:
5969						case BP_VAR_UNSET:
5970							|	// retval = &EG(uninitialized_zval);
5971							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
5972							|	jmp >9
5973							break;
5974						default:
5975							ZEND_UNREACHABLE();
5976					}
5977					|.code
5978				}
5979				break;
5980			case BP_VAR_RW:
5981				if (opline->op2_type != IS_CONST) {
5982					|	EXT_CALL zend_jit_symtable_lookup_rw, r0
5983				} else {
5984					|	EXT_CALL zend_jit_hash_lookup_rw, r0
5985				}
5986				|	test r0, r0
5987				if (not_found_exit_addr) {
5988					|	jz &not_found_exit_addr
5989				} else {
5990					|	jz >9
5991				}
5992				break;
5993			case BP_VAR_W:
5994				if (opline->op2_type != IS_CONST) {
5995					|	EXT_CALL zend_jit_symtable_lookup_w, r0
5996				} else {
5997					|	EXT_CALL zend_hash_lookup, r0
5998				}
5999				break;
6000			default:
6001				ZEND_UNREACHABLE();
6002		}
6003	}
6004
6005	if (type == BP_JIT_IS && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))) {
6006	    |5:
6007		if (op1_info & MAY_BE_ARRAY_OF_REF) {
6008			|	ZVAL_DEREF r0, MAY_BE_REF
6009		}
6010		|	cmp byte [r0 + 8], IS_NULL
6011		if (not_found_exit_addr) {
6012			|	jle &not_found_exit_addr
6013		} else if (found_exit_addr) {
6014			|	jg &found_exit_addr
6015		} else {
6016			|	jle >9 // NOT FOUND
6017		}
6018	}
6019
6020	if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
6021		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6022			|.cold_code
6023			|3:
6024		}
6025		if (type != BP_VAR_RW) {
6026			|	SET_EX_OPLINE opline, r0
6027		}
6028		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
6029		switch (type) {
6030			case BP_VAR_R:
6031				|.if X64
6032					|   LOAD_ZVAL_ADDR CARG3, res_addr
6033				|.else
6034					|	sub r4, 12
6035					|   PUSH_ZVAL_ADDR res_addr, r0
6036				|.endif
6037				|	EXT_CALL zend_jit_fetch_dim_r_helper, r0
6038				|.if not(X64)
6039				|	add r4, 12
6040				|.endif
6041				|	jmp >9
6042				break;
6043			case BP_JIT_IS:
6044				|	EXT_CALL zend_jit_fetch_dim_isset_helper, r0
6045				|	test r0, r0
6046				if (not_found_exit_addr) {
6047					|	je &not_found_exit_addr
6048					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6049						|	jmp >8
6050					}
6051				} else if (found_exit_addr) {
6052					|	jne &found_exit_addr
6053					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6054						|	jmp >9
6055					}
6056				} else {
6057					|	jne >8
6058					|	jmp >9
6059				}
6060				break;
6061			case BP_VAR_IS:
6062			case BP_VAR_UNSET:
6063				|.if X64
6064					|   LOAD_ZVAL_ADDR CARG3, res_addr
6065				|.else
6066					|	sub r4, 12
6067					|   PUSH_ZVAL_ADDR res_addr, r0
6068				|.endif
6069				|	EXT_CALL zend_jit_fetch_dim_is_helper, r0
6070				|.if not(X64)
6071				|	add r4, 12
6072				|.endif
6073				|	jmp >9
6074				break;
6075			case BP_VAR_RW:
6076				|	EXT_CALL zend_jit_fetch_dim_rw_helper, r0
6077				|	test r0, r0
6078				|	jne >8
6079				|	jmp >9
6080				break;
6081			case BP_VAR_W:
6082				|	EXT_CALL zend_jit_fetch_dim_w_helper, r0
6083				|	test r0, r0
6084				|	jne >8
6085				|	jmp >9
6086				break;
6087			default:
6088				ZEND_UNREACHABLE();
6089		}
6090		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6091			|.code
6092		}
6093	}
6094
6095	return 1;
6096}
6097
6098static int zend_jit_simple_assign(dasm_State    **Dst,
6099                                  const zend_op  *opline,
6100                                  zend_jit_addr   var_addr,
6101                                  uint32_t        var_info,
6102                                  uint32_t        var_def_info,
6103                                  uint8_t      val_type,
6104                                  zend_jit_addr   val_addr,
6105                                  uint32_t        val_info,
6106                                  zend_jit_addr   res_addr,
6107                                  int             in_cold,
6108                                  int             save_r1,
6109                                  bool            check_exception)
6110/* Labels: 1,2,3 */
6111{
6112	zend_reg tmp_reg;
6113
6114	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_addr) != ZREG_R0) {
6115		tmp_reg = ZREG_R0;
6116	} else {
6117		/* ASSIGN_DIM */
6118		tmp_reg = ZREG_FCARG1;
6119	}
6120
6121	if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
6122		zval *zv = Z_ZV(val_addr);
6123
6124		if (!res_addr) {
6125			|	ZVAL_COPY_CONST var_addr, var_info, var_def_info, zv, tmp_reg
6126		} else {
6127			|	ZVAL_COPY_CONST_2 var_addr, res_addr, var_info, var_def_info, zv, tmp_reg
6128		}
6129		if (Z_REFCOUNTED_P(zv)) {
6130			if (!res_addr) {
6131				|	ADDREF_CONST zv, Ra(tmp_reg)
6132			} else {
6133				|	ADDREF_CONST_2 zv, Ra(tmp_reg)
6134			}
6135		}
6136	} else {
6137		if (val_info & MAY_BE_UNDEF) {
6138			if (in_cold) {
6139				|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >2
6140			} else {
6141				|	IF_ZVAL_TYPE val_addr, IS_UNDEF, >1
6142				|.cold_code
6143				|1:
6144			}
6145			|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
6146			if (save_r1) {
6147				|	mov aword T1, FCARG1a // save
6148			}
6149			|	SET_ZVAL_TYPE_INFO var_addr, IS_NULL
6150			if (res_addr) {
6151				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
6152			}
6153			if (opline) {
6154				|	SET_EX_OPLINE opline, Ra(tmp_reg)
6155			}
6156			ZEND_ASSERT(Z_MODE(val_addr) == IS_MEM_ZVAL && Z_REG(val_addr) == ZREG_FP);
6157			|	mov FCARG1d, Z_OFFSET(val_addr)
6158			|	EXT_CALL zend_jit_undefined_op_helper, r0
6159			if (check_exception) {
6160				|	test r0, r0
6161				|	jz ->exception_handler_undef
6162			}
6163			if (save_r1) {
6164				|	mov FCARG1a, aword T1 // restore
6165			}
6166			|	jmp >3
6167			if (in_cold) {
6168				|2:
6169			} else {
6170				|.code
6171			}
6172		}
6173		if (val_info & MAY_BE_REF) {
6174			if (val_type == IS_CV) {
6175				ZEND_ASSERT(Z_REG(var_addr) != ZREG_R2);
6176				if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_R2 || Z_OFFSET(val_addr) != 0) {
6177					|	LOAD_ZVAL_ADDR r2, val_addr
6178				}
6179				|	ZVAL_DEREF r2, val_info
6180				val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R2, 0);
6181			} else {
6182				zend_jit_addr ref_addr;
6183				zend_reg type_reg = tmp_reg;
6184
6185				if (in_cold) {
6186					|	IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, >1
6187				} else {
6188					|	IF_ZVAL_TYPE val_addr, IS_REFERENCE, >1
6189					|.cold_code
6190					|1:
6191				}
6192				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
6193				|	GET_ZVAL_PTR r2, val_addr
6194				|	GC_DELREF r2
6195				|	// ZVAL_COPY_VALUE(return_value, &ref->value);
6196				ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R2, 8);
6197				if (!res_addr) {
6198					|	ZVAL_COPY_VALUE var_addr, var_info, ref_addr, val_info, type_reg, tmp_reg
6199				} else {
6200					|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, ref_addr, val_info, type_reg, tmp_reg
6201				}
6202				|	je >2
6203				if (tmp_reg == ZREG_R0) {
6204					|	IF_NOT_REFCOUNTED ah, >3
6205				} else {
6206					|	IF_NOT_FLAGS Rd(tmp_reg), (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), >3
6207				}
6208				|	GET_ZVAL_PTR Ra(tmp_reg), var_addr
6209
6210				if (!res_addr) {
6211					|	GC_ADDREF Ra(tmp_reg)
6212				} else {
6213					|	add dword [Ra(tmp_reg)], 2
6214				}
6215				|	jmp >3
6216				|2:
6217				if (res_addr) {
6218					if (tmp_reg == ZREG_R0) {
6219						|	IF_NOT_REFCOUNTED ah, >2
6220					} else {
6221						|	IF_NOT_FLAGS Rd(tmp_reg), (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), >2
6222					}
6223					|	GET_ZVAL_PTR Ra(tmp_reg), var_addr
6224					|	GC_ADDREF Ra(tmp_reg)
6225					|2:
6226				}
6227				if (save_r1) {
6228					|	mov aword T1, FCARG1a // save
6229				}
6230				|	EFREE_REFERENCE r2
6231				if (save_r1) {
6232					|	mov FCARG1a, aword T1 // restore
6233				}
6234				|	jmp >3
6235				if (in_cold) {
6236					|1:
6237				} else {
6238					|.code
6239				}
6240			}
6241		}
6242
6243		if (!res_addr) {
6244			|	ZVAL_COPY_VALUE var_addr, var_info, val_addr, val_info, ZREG_R2, tmp_reg
6245		} else {
6246			|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, val_addr, val_info, ZREG_R2, tmp_reg
6247		}
6248
6249		if (val_type == IS_CV) {
6250			if (!res_addr) {
6251				|	TRY_ADDREF val_info, dh, Ra(tmp_reg)
6252			} else {
6253				|	TRY_ADDREF_2 val_info, dh, Ra(tmp_reg)
6254			}
6255		} else {
6256			if (res_addr) {
6257				|	TRY_ADDREF val_info, dh, Ra(tmp_reg)
6258			}
6259		}
6260		|3:
6261	}
6262	return 1;
6263}
6264
6265static int zend_jit_assign_to_typed_ref(dasm_State         **Dst,
6266                                       const zend_op        *opline,
6267                                       uint8_t            val_type,
6268                                       zend_jit_addr         val_addr,
6269                                       zend_jit_addr         res_addr,
6270                                       bool                  check_exception)
6271{
6272	|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
6273	|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
6274	|	jnz >2
6275	|.cold_code
6276	|2:
6277	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
6278		|	LOAD_ZVAL_ADDR FCARG2a, val_addr
6279	}
6280	if (opline) {
6281		|	SET_EX_OPLINE opline, r0
6282	}
6283	if (!res_addr) {
6284		if (val_type == IS_CONST) {
6285			|	EXT_CALL zend_jit_assign_const_to_typed_ref, r0
6286		} else if (val_type == IS_TMP_VAR) {
6287			|	EXT_CALL zend_jit_assign_tmp_to_typed_ref, r0
6288		} else if (val_type == IS_VAR) {
6289			|	EXT_CALL zend_jit_assign_var_to_typed_ref, r0
6290		} else if (val_type == IS_CV) {
6291			|	EXT_CALL zend_jit_assign_cv_to_typed_ref, r0
6292		} else {
6293			ZEND_UNREACHABLE();
6294		}
6295	} else {
6296		|.if X64
6297			|	LOAD_ZVAL_ADDR CARG3, res_addr
6298		|.else
6299			|	sub r4, 12
6300			|	PUSH_ZVAL_ADDR res_addr, r0
6301		|.endif
6302		if (val_type == IS_CONST) {
6303			|	EXT_CALL zend_jit_assign_const_to_typed_ref2, r0
6304		} else if (val_type == IS_TMP_VAR) {
6305			|	EXT_CALL zend_jit_assign_tmp_to_typed_ref2, r0
6306		} else if (val_type == IS_VAR) {
6307			|	EXT_CALL zend_jit_assign_var_to_typed_ref2, r0
6308		} else if (val_type == IS_CV) {
6309			|	EXT_CALL zend_jit_assign_cv_to_typed_ref2, r0
6310		} else {
6311			ZEND_UNREACHABLE();
6312		}
6313		|.if not(X64)
6314		|	add r4, 12
6315		|.endif
6316	}
6317	if (check_exception) {
6318		|	// if (UNEXPECTED(EG(exception) != NULL)) {
6319		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
6320		|	je >8  // END OF zend_jit_assign_to_variable()
6321		|	jmp ->exception_handler
6322	} else {
6323		|	jmp >8
6324	}
6325	|.code
6326
6327	return 1;
6328}
6329
6330static int zend_jit_assign_to_variable_call(dasm_State    **Dst,
6331                                            const zend_op  *opline,
6332                                            zend_jit_addr   __var_use_addr,
6333                                            zend_jit_addr   var_addr,
6334                                            uint32_t        __var_info,
6335                                            uint32_t        __var_def_info,
6336                                            uint8_t      val_type,
6337                                            zend_jit_addr   val_addr,
6338                                            uint32_t        val_info,
6339                                            zend_jit_addr   __res_addr,
6340                                            bool       __check_exception)
6341{
6342	if (val_info & MAY_BE_UNDEF) {
6343		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
6344			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
6345			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6346
6347			if (!exit_addr) {
6348				return 0;
6349			}
6350
6351			|	IF_ZVAL_TYPE val_addr, IS_UNDEF, &exit_addr
6352		} else {
6353			|	IF_ZVAL_TYPE val_addr, IS_UNDEF, >1
6354			|.cold_code
6355			|1:
6356			ZEND_ASSERT(Z_REG(val_addr) == ZREG_FP);
6357			if (Z_REG(var_addr) != ZREG_FP) {
6358				|	mov aword T1, Ra(Z_REG(var_addr)) // save
6359			}
6360			|	SET_EX_OPLINE opline, r0
6361			|	mov FCARG1d, Z_OFFSET(val_addr)
6362			|	EXT_CALL zend_jit_undefined_op_helper, r0
6363			if (Z_REG(var_addr) != ZREG_FP) {
6364				|	mov Ra(Z_REG(var_addr)), aword T1 // restore
6365			}
6366			if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
6367				|	LOAD_ZVAL_ADDR FCARG1a, var_addr
6368			}
6369			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
6370			|	call ->assign_const
6371			|	jmp >9
6372			|.code
6373		}
6374	}
6375	if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
6376		|	LOAD_ZVAL_ADDR FCARG1a, var_addr
6377	}
6378	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
6379		|	LOAD_ZVAL_ADDR FCARG2a, val_addr
6380	}
6381	if (opline) {
6382		|	SET_EX_OPLINE opline, r0
6383	}
6384	if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
6385		|	call ->assign_tmp
6386	} else if (val_type == IS_CONST) {
6387		|	call ->assign_const
6388	} else if (val_type == IS_TMP_VAR) {
6389		|	call ->assign_tmp
6390	} else if (val_type == IS_VAR) {
6391		if (!(val_info & MAY_BE_REF)) {
6392			|	call ->assign_tmp
6393		} else {
6394			|	call ->assign_var
6395		}
6396	} else if (val_type == IS_CV) {
6397		if (!(val_info & MAY_BE_REF)) {
6398			|	call ->assign_cv_noref
6399		} else {
6400			|	call ->assign_cv
6401		}
6402		if ((val_info & MAY_BE_UNDEF) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
6403			|9:
6404		}
6405	} else {
6406		ZEND_UNREACHABLE();
6407	}
6408
6409	return 1;
6410}
6411
6412static int zend_jit_assign_to_variable(dasm_State    **Dst,
6413                                       const zend_op  *opline,
6414                                       zend_jit_addr   var_use_addr,
6415                                       zend_jit_addr   var_addr,
6416                                       uint32_t        var_info,
6417                                       uint32_t        var_def_info,
6418                                       uint8_t      val_type,
6419                                       zend_jit_addr   val_addr,
6420                                       uint32_t        val_info,
6421                                       zend_jit_addr   res_addr,
6422                                       bool       check_exception)
6423/* Labels: 1,2,3,4,5,8 */
6424{
6425	int done = 0;
6426	zend_reg ref_reg, tmp_reg;
6427
6428	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_use_addr) != ZREG_R0) {
6429		ref_reg = ZREG_FCARG1;
6430		tmp_reg = ZREG_R0;
6431	} else {
6432		/* ASSIGN_DIM */
6433		ref_reg = ZREG_R0;
6434		tmp_reg = ZREG_FCARG1;
6435	}
6436
6437	if (var_info & MAY_BE_REF) {
6438		if (Z_MODE(var_use_addr) != IS_MEM_ZVAL || Z_REG(var_use_addr) != ref_reg || Z_OFFSET(var_use_addr) != 0) {
6439			|	LOAD_ZVAL_ADDR Ra(ref_reg), var_use_addr
6440			var_addr = var_use_addr = ZEND_ADDR_MEM_ZVAL(ref_reg, 0);
6441		}
6442		|	// if (Z_ISREF_P(variable_ptr)) {
6443		|	IF_NOT_Z_TYPE, Ra(ref_reg), IS_REFERENCE, >3
6444		|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
6445		|	GET_Z_PTR FCARG1a, Ra(ref_reg)
6446		if (!zend_jit_assign_to_typed_ref(Dst, opline, val_type, val_addr, res_addr, check_exception)) {
6447			return 0;
6448		}
6449		|	lea Ra(ref_reg), [FCARG1a + offsetof(zend_reference, val)]
6450		|3:
6451	}
6452	if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6453		if (RC_MAY_BE_1(var_info)) {
6454			int in_cold = 0;
6455
6456			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6457				|	IF_ZVAL_REFCOUNTED var_use_addr, >1
6458				|.cold_code
6459				|1:
6460				in_cold = 1;
6461			}
6462			if (Z_REG(var_use_addr) == ZREG_FCARG1 || Z_REG(var_use_addr) == ZREG_R0) {
6463				bool keep_gc = 0;
6464
6465				|	GET_ZVAL_PTR Ra(tmp_reg), var_use_addr
6466				if (tmp_reg == ZREG_FCARG1) {
6467					if (Z_MODE(val_addr) == IS_REG) {
6468						keep_gc = 1;
6469					} else if ((val_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) == 0) {
6470						keep_gc = 1;
6471					} else if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
6472						if (sizeof(void*) == 4) {
6473							keep_gc = 1;
6474						} else {
6475							zval *zv = Z_ZV(val_addr);
6476
6477							if (Z_TYPE_P(zv) == IS_DOUBLE) {
6478								if (Z_DVAL_P(zv) == 0 || IS_SIGNED_32BIT(zv)) {
6479									keep_gc = 1;
6480								}
6481							} else if (IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
6482								keep_gc = 1;
6483							}
6484						}
6485					} else if (Z_MODE(val_addr) == IS_MEM_ZVAL) {
6486						if ((val_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
6487							keep_gc = 1;
6488						}
6489					}
6490				}
6491				if (!keep_gc) {
6492					|	mov aword T1, Ra(tmp_reg) // save
6493				}
6494				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, 0)) {
6495					return 0;
6496				}
6497				if (!keep_gc) {
6498					|	mov FCARG1a, aword T1 // restore
6499				}
6500			} else {
6501				|	GET_ZVAL_PTR FCARG1a, var_use_addr
6502				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, 0)) {
6503					return 0;
6504				}
6505			}
6506			|	GC_DELREF FCARG1a
6507			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
6508				|	jnz >4
6509			} else {
6510				|	jnz >8
6511			}
6512			|	ZVAL_DTOR_FUNC var_info, opline
6513			if (in_cold || (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0)) {
6514				if (check_exception && !(val_info & MAY_BE_UNDEF)) {
6515					|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
6516					|	je >8
6517					|	jmp ->exception_handler
6518				} else {
6519					|	jmp >8
6520				}
6521			}
6522			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
6523				|4:
6524				|	IF_GC_MAY_NOT_LEAK FCARG1a, >8
6525				|	EXT_CALL gc_possible_root, r0
6526				if (in_cold) {
6527					|	jmp >8
6528				}
6529			}
6530			if (check_exception && (val_info & MAY_BE_UNDEF)) {
6531				|8:
6532				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
6533				|	je >8
6534				|	jmp ->exception_handler
6535			}
6536			if (in_cold) {
6537				|.code
6538			} else {
6539				done = 1;
6540			}
6541		} else /* if (RC_MAY_BE_N(var_info)) */ {
6542			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6543				|	IF_NOT_ZVAL_REFCOUNTED var_use_addr, >5
6544			}
6545			if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
6546				if (Z_REG(var_use_addr) != ZREG_FP) {
6547					|	mov T1, Ra(Z_REG(var_use_addr)) // save
6548				}
6549				|	GET_ZVAL_PTR FCARG1a, var_use_addr
6550				|	GC_DELREF FCARG1a
6551				|	IF_GC_MAY_NOT_LEAK FCARG1a, >5
6552				|	EXT_CALL gc_possible_root, r0
6553				if (Z_REG(var_use_addr) != ZREG_FP) {
6554					|	mov Ra(Z_REG(var_use_addr)), T1 // restore
6555				}
6556			} else {
6557				|	GET_ZVAL_PTR Ra(tmp_reg), var_use_addr
6558				|	GC_DELREF Ra(tmp_reg)
6559			}
6560			|5:
6561	    }
6562	}
6563
6564	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, check_exception)) {
6565		return 0;
6566	}
6567
6568	|8:
6569
6570	return 1;
6571}
6572
6573static 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, uint8_t dim_type, int may_throw)
6574{
6575	zend_jit_addr op2_addr, op3_addr, res_addr;
6576
6577	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
6578	op3_addr = OP1_DATA_ADDR();
6579	if (opline->result_type == IS_UNUSED) {
6580		res_addr = 0;
6581	} else {
6582		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
6583	}
6584
6585	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (val_info & MAY_BE_UNDEF)) {
6586		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
6587		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6588
6589		if (!exit_addr) {
6590			return 0;
6591		}
6592
6593		|	IF_ZVAL_TYPE op3_addr, IS_UNDEF, &exit_addr
6594
6595		val_info &= ~MAY_BE_UNDEF;
6596	}
6597
6598	if (op1_info & MAY_BE_REF) {
6599		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
6600		|	IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
6601		|	GET_Z_PTR FCARG2a, FCARG1a
6602		|	IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
6603		|	lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
6604		|	jmp >3
6605		|.cold_code
6606		|2:
6607		|	SET_EX_OPLINE opline, r0
6608		|	EXT_CALL zend_jit_prepare_assign_dim_ref, r0
6609		|	test r0, r0
6610		|	mov FCARG1a, r0
6611		|	jne >1
6612		|	jmp ->exception_handler_undef
6613		|.code
6614		|1:
6615		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
6616	}
6617
6618	if (op1_info & MAY_BE_ARRAY) {
6619		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
6620			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
6621		}
6622		|3:
6623		|	SEPARATE_ARRAY op1_addr, op1_info, 1
6624	} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
6625		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6626			|	CMP_ZVAL_TYPE op1_addr, IS_NULL
6627			|	jg >7
6628		}
6629		|	// ZVAL_ARR(container, zend_new_array(8));
6630		if (Z_REG(op1_addr) != ZREG_FP) {
6631			|	mov T1, Ra(Z_REG(op1_addr)) // save
6632		}
6633		|	EXT_CALL _zend_new_array_0, r0
6634		if (Z_REG(op1_addr) != ZREG_FP) {
6635			|	mov Ra(Z_REG(op1_addr)), T1 // restore
6636		}
6637		|	SET_ZVAL_LVAL op1_addr, r0
6638		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
6639		|	mov FCARG1a, r0
6640	}
6641
6642	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6643		|6:
6644		if (opline->op2_type == IS_UNUSED) {
6645			uint32_t var_info = MAY_BE_NULL;
6646			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
6647
6648			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
6649			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
6650			|	EXT_CALL zend_hash_next_index_insert, r0
6651			|	// if (UNEXPECTED(!var_ptr)) {
6652			|	test r0, r0
6653			|	jz >1
6654			|.cold_code
6655			|1:
6656			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
6657			|	CANNOT_ADD_ELEMENT opline
6658			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
6659			|	jmp >9
6660			|.code
6661
6662			if (!zend_jit_simple_assign(Dst, opline, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0, 0, 0)) {
6663				return 0;
6664			}
6665		} else {
6666			uint32_t var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
6667			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
6668
6669			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_W, op1_info, op2_info, dim_type, NULL, NULL, NULL)) {
6670				return 0;
6671			}
6672
6673			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
6674				var_info |= MAY_BE_REF;
6675			}
6676			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6677				var_info |= MAY_BE_RC1;
6678			}
6679
6680			|8:
6681			|	// value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE);
6682			if (opline->op1_type == IS_VAR) {
6683				ZEND_ASSERT(opline->result_type == IS_UNUSED);
6684				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)) {
6685					return 0;
6686				}
6687			} else {
6688				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)) {
6689					return 0;
6690				}
6691			}
6692		}
6693	}
6694
6695	if (((op1_info & MAY_BE_ARRAY) &&
6696	     (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) ||
6697	    (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY)))) {
6698		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6699			|.cold_code
6700			|7:
6701		}
6702
6703		if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) &&
6704		    (op1_info & MAY_BE_ARRAY)) {
6705			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6706				|	CMP_ZVAL_TYPE op1_addr, IS_NULL
6707				|	jg >2
6708			}
6709			|	// ZVAL_ARR(container, zend_new_array(8));
6710			if (Z_REG(op1_addr) != ZREG_FP) {
6711				|	mov T1, Ra(Z_REG(op1_addr)) // save
6712			}
6713			|	EXT_CALL _zend_new_array_0, r0
6714			if (Z_REG(op1_addr) != ZREG_FP) {
6715				|	mov Ra(Z_REG(op1_addr)), T1 // restore
6716			}
6717			|	SET_ZVAL_LVAL op1_addr, r0
6718			|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
6719			|	mov FCARG1a, r0
6720			|	// ZEND_VM_C_GOTO(assign_dim_op_new_array);
6721			|	jmp <6
6722			|2:
6723		}
6724
6725		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6726			|	SET_EX_OPLINE opline, r0
6727		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
6728				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
6729			}
6730		    if (opline->op2_type == IS_UNUSED) {
6731				|	xor FCARG2a, FCARG2a
6732			} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
6733				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
6734				|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
6735			} else {
6736				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
6737			}
6738			|.if not(X64)
6739			|	sub r4, 8
6740			|.endif
6741			if (opline->result_type == IS_UNUSED) {
6742				|.if X64
6743					|	xor CARG4, CARG4
6744				|.else
6745					|	push 0
6746				|.endif
6747			} else {
6748				|.if X64
6749					|	LOAD_ZVAL_ADDR CARG4, res_addr
6750				|.else
6751					|	PUSH_ZVAL_ADDR res_addr, r0
6752				|.endif
6753			}
6754			|.if X64
6755				|	LOAD_ZVAL_ADDR CARG3, op3_addr
6756			|.else
6757				|	PUSH_ZVAL_ADDR op3_addr, r0
6758			|.endif
6759			|	EXT_CALL zend_jit_assign_dim_helper, r0
6760			|.if not(X64)
6761			|	add r4, 8
6762			|.endif
6763
6764#ifdef ZEND_JIT_USE_RC_INFERENCE
6765			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR)) && (val_info & MAY_BE_RC1)) {
6766				/* ASSIGN_DIM may increase refcount of the value */
6767				val_info |= MAY_BE_RCN;
6768			}
6769#endif
6770
6771			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, NULL
6772		}
6773
6774		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6775			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6776				|	jmp >9 // END
6777			}
6778			|.code
6779		}
6780	}
6781
6782#ifdef ZEND_JIT_USE_RC_INFERENCE
6783	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))) {
6784		/* ASSIGN_DIM may increase refcount of the key */
6785		op2_info |= MAY_BE_RCN;
6786	}
6787#endif
6788
6789	|9:
6790	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
6791
6792	if (may_throw) {
6793		zend_jit_check_exception(Dst);
6794	}
6795
6796	return 1;
6797}
6798
6799static 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, uint8_t dim_type, int may_throw)
6800{
6801	zend_jit_addr op2_addr, op3_addr, var_addr;
6802	const void *not_found_exit_addr = NULL;
6803	uint32_t var_info = MAY_BE_NULL;
6804
6805	ZEND_ASSERT(opline->result_type == IS_UNUSED);
6806
6807	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
6808	op3_addr = OP1_DATA_ADDR();
6809
6810	|	SET_EX_OPLINE opline, r0
6811	if (op1_info & MAY_BE_REF) {
6812		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
6813		|	IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
6814		|	GET_Z_PTR FCARG2a, FCARG1a
6815		|	IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
6816		|	lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
6817		|	jmp >3
6818		|.cold_code
6819		|2:
6820		|	EXT_CALL zend_jit_prepare_assign_dim_ref, r0
6821		|	test r0, r0
6822		|	mov FCARG1a, r0
6823		|	jne >1
6824		|	jmp ->exception_handler_undef
6825		|.code
6826		|1:
6827		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
6828	}
6829
6830	if (op1_info & MAY_BE_ARRAY) {
6831		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
6832			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
6833		}
6834		|3:
6835		|	SEPARATE_ARRAY op1_addr, op1_info, 1
6836	}
6837	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
6838		if (op1_info & MAY_BE_ARRAY) {
6839			|.cold_code
6840			|7:
6841		}
6842		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6843			|	CMP_ZVAL_TYPE op1_addr, IS_NULL
6844			|	jg >7
6845		}
6846		if (Z_REG(op1_addr) != ZREG_FP) {
6847			|	mov T1, Ra(Z_REG(op1_addr)) // save
6848		}
6849		if (op1_info & MAY_BE_UNDEF) {
6850			if (op1_info & MAY_BE_NULL) {
6851				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
6852			}
6853			|	mov FCARG1a, opline->op1.var
6854			|	EXT_CALL zend_jit_undefined_op_helper, r0
6855			|1:
6856		}
6857		|	// ZVAL_ARR(container, zend_new_array(8));
6858		|	EXT_CALL _zend_new_array_0, r0
6859		if (Z_REG(op1_addr) != ZREG_FP) {
6860			|	mov Ra(Z_REG(op1_addr)), T1 // restore
6861		}
6862		|	SET_ZVAL_LVAL op1_addr, r0
6863		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
6864		|	mov FCARG1a, r0
6865		if (op1_info & MAY_BE_ARRAY) {
6866			|	jmp >1
6867			|.code
6868			|1:
6869		}
6870	}
6871
6872	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6873		uint32_t var_def_info = zend_array_element_type(op1_def_info, opline->op1_type, 1, 0);
6874
6875		|6:
6876		if (opline->op2_type == IS_UNUSED) {
6877			var_info = MAY_BE_NULL;
6878
6879			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
6880			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
6881			|	EXT_CALL zend_hash_next_index_insert, r0
6882			|	// if (UNEXPECTED(!var_ptr)) {
6883			|	test r0, r0
6884			|	jz >1
6885			|.cold_code
6886			|1:
6887			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
6888			|	CANNOT_ADD_ELEMENT opline
6889			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
6890			|	jmp >9
6891			|.code
6892		} else {
6893			var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
6894			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
6895				var_info |= MAY_BE_REF;
6896			}
6897			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6898				var_info |= MAY_BE_RC1;
6899			}
6900
6901			if (dim_type != IS_UNKNOWN
6902			 && dim_type != IS_UNDEF
6903			 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY
6904			 && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))
6905			 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
6906				int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
6907				not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6908				if (!not_found_exit_addr) {
6909					return 0;
6910				}
6911			}
6912
6913			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, dim_type, NULL, not_found_exit_addr, NULL)) {
6914				return 0;
6915			}
6916
6917			|8:
6918			if (not_found_exit_addr && dim_type != IS_REFERENCE) {
6919				|	IF_NOT_Z_TYPE, r0, dim_type, &not_found_exit_addr
6920				var_info = (1 << dim_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
6921			}
6922			if (var_info & MAY_BE_REF) {
6923				binary_op_type binary_op = get_binary_op(opline->extended_value);
6924				|	IF_NOT_Z_TYPE, r0, IS_REFERENCE, >1
6925				|	GET_Z_PTR FCARG1a, r0
6926				|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
6927				|	jnz >2
6928				|	lea r0, aword [FCARG1a + offsetof(zend_reference, val)]
6929				|.cold_code
6930				|2:
6931				|	LOAD_ZVAL_ADDR FCARG2a, op3_addr
6932				|.if X64
6933					|	LOAD_ADDR CARG3, binary_op
6934				|.else
6935					|	sub r4, 12
6936					|	PUSH_ADDR binary_op, r0
6937				|.endif
6938				if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
6939				 && (op1_data_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6940					|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
6941				} else {
6942					|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
6943				}
6944				|.if not(X64)
6945				|	add r4, 12
6946				|.endif
6947				|	jmp >9
6948				|.code
6949				|1:
6950			}
6951		}
6952
6953		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
6954		switch (opline->extended_value) {
6955			case ZEND_ADD:
6956			case ZEND_SUB:
6957			case ZEND_MUL:
6958			case ZEND_DIV:
6959				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,
6960						1 /* may overflow */, may_throw)) {
6961					return 0;
6962				}
6963				break;
6964			case ZEND_BW_OR:
6965			case ZEND_BW_AND:
6966			case ZEND_BW_XOR:
6967			case ZEND_SL:
6968			case ZEND_SR:
6969			case ZEND_MOD:
6970				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
6971						IS_CV, opline->op1, var_addr, var_info, NULL,
6972						(opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info,
6973						op1_data_range,
6974						0, var_addr, var_def_info, var_info, may_throw)) {
6975					return 0;
6976				}
6977				break;
6978			case ZEND_CONCAT:
6979				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,
6980						may_throw)) {
6981					return 0;
6982				}
6983				break;
6984			default:
6985				ZEND_UNREACHABLE();
6986		}
6987		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
6988	}
6989
6990	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6991		binary_op_type binary_op;
6992
6993		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6994			|.cold_code
6995			|7:
6996		}
6997
6998		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
6999			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
7000		}
7001	    if (opline->op2_type == IS_UNUSED) {
7002			|	xor FCARG2a, FCARG2a
7003		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
7004			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
7005			|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
7006		} else {
7007			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
7008		}
7009		binary_op = get_binary_op(opline->extended_value);
7010		|.if X64
7011			|	LOAD_ZVAL_ADDR CARG3, op3_addr
7012			|	LOAD_ADDR CARG4, binary_op
7013		|.else
7014			|	sub r4, 8
7015			|	PUSH_ADDR binary_op, r0
7016			|	PUSH_ZVAL_ADDR op3_addr, r0
7017		|.endif
7018		|	EXT_CALL zend_jit_assign_dim_op_helper, r0
7019		|.if not(X64)
7020		|	add r4, 8
7021		|.endif
7022
7023		|9:
7024		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, NULL
7025		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL
7026		if (may_throw) {
7027			zend_jit_check_exception(Dst);
7028		}
7029
7030		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
7031			|	jmp >9 // END
7032			|.code
7033			|9:
7034		}
7035	} else if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY))
7036			&& (!not_found_exit_addr || (var_info & MAY_BE_REF))) {
7037		|.cold_code
7038		|9:
7039		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, opline
7040		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
7041		if (may_throw) {
7042			zend_jit_check_exception(Dst);
7043		}
7044		|	jmp >9
7045		|.code
7046		|9:
7047	}
7048
7049	return 1;
7050}
7051
7052static 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)
7053{
7054	zend_jit_addr op1_addr, op2_addr;
7055
7056	ZEND_ASSERT(opline->op1_type == IS_CV && opline->result_type == IS_UNUSED);
7057	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
7058
7059	op1_addr = OP1_ADDR();
7060	op2_addr = OP2_ADDR();
7061
7062	if (op1_info & MAY_BE_REF) {
7063		binary_op_type binary_op = get_binary_op(opline->extended_value);
7064		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
7065		|	IF_NOT_Z_TYPE, FCARG1a, IS_REFERENCE, >1
7066		|	GET_Z_PTR FCARG1a, FCARG1a
7067		|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
7068		|	jnz >2
7069		|	add FCARG1a, offsetof(zend_reference, val)
7070		|.cold_code
7071		|2:
7072		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
7073		|.if X64
7074			|	LOAD_ADDR CARG3, binary_op
7075		|.else
7076			|	sub r4, 12
7077			|	PUSH_ADDR binary_op, r0
7078		|.endif
7079		|	SET_EX_OPLINE opline, r0
7080		if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
7081		 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
7082			|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
7083		} else {
7084			|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
7085		}
7086		|.if not(X64)
7087		|	add r4, 12
7088		|.endif
7089		zend_jit_check_exception(Dst);
7090		|	jmp >9
7091		|.code
7092		|1:
7093		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
7094	}
7095
7096	int result;
7097	switch (opline->extended_value) {
7098		case ZEND_ADD:
7099		case ZEND_SUB:
7100		case ZEND_MUL:
7101		case ZEND_DIV:
7102			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);
7103			break;
7104		case ZEND_BW_OR:
7105		case ZEND_BW_AND:
7106		case ZEND_BW_XOR:
7107		case ZEND_SL:
7108		case ZEND_SR:
7109		case ZEND_MOD:
7110			result = zend_jit_long_math_helper(Dst, opline, opline->extended_value,
7111				opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
7112				opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
7113				opline->op1.var, op1_addr, op1_def_info, op1_info, may_throw);
7114			break;
7115		case ZEND_CONCAT:
7116			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);
7117			break;
7118		default:
7119			ZEND_UNREACHABLE();
7120	}
7121	|9:
7122	return result;
7123}
7124
7125static int zend_jit_cmp_long_long(dasm_State    **Dst,
7126                                  const zend_op  *opline,
7127                                  zend_ssa_range *op1_range,
7128                                  zend_jit_addr   op1_addr,
7129                                  zend_ssa_range *op2_range,
7130                                  zend_jit_addr   op2_addr,
7131                                  zend_jit_addr   res_addr,
7132                                  uint8_t      smart_branch_opcode,
7133                                  uint32_t        target_label,
7134                                  uint32_t        target_label2,
7135                                  const void     *exit_addr,
7136                                  bool       skip_comparison)
7137{
7138	bool swap = 0;
7139	bool result;
7140
7141	if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) {
7142		if (!smart_branch_opcode ||
7143		    smart_branch_opcode == ZEND_JMPZ_EX ||
7144		    smart_branch_opcode == ZEND_JMPNZ_EX) {
7145			|	SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE)
7146		}
7147		if (smart_branch_opcode && !exit_addr) {
7148			if (smart_branch_opcode == ZEND_JMPZ ||
7149			    smart_branch_opcode == ZEND_JMPZ_EX) {
7150				if (!result) {
7151					| jmp => target_label
7152				}
7153			} else if (smart_branch_opcode == ZEND_JMPNZ ||
7154			           smart_branch_opcode == ZEND_JMPNZ_EX) {
7155				if (result) {
7156					| jmp => target_label
7157				}
7158			} else {
7159				ZEND_UNREACHABLE();
7160			}
7161		}
7162		return 1;
7163	}
7164
7165	if (skip_comparison) {
7166		if (Z_MODE(op1_addr) != IS_REG &&
7167		    (Z_MODE(op2_addr) == IS_REG ||
7168		     (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL))) {
7169			swap = 1;
7170		}
7171	} else if (Z_MODE(op1_addr) == IS_REG) {
7172		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
7173			|	test Ra(Z_REG(op1_addr)), Ra(Z_REG(op1_addr))
7174		} else {
7175			|	LONG_OP cmp, Z_REG(op1_addr), op2_addr, r0
7176		}
7177	} else if (Z_MODE(op2_addr) == IS_REG) {
7178		if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 0) {
7179			|	test Ra(Z_REG(op2_addr)), Ra(Z_REG(op2_addr))
7180		} else {
7181			|	LONG_OP cmp, Z_REG(op2_addr), op1_addr, r0
7182		}
7183		swap = 1;
7184	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL) {
7185		|	LONG_OP_WITH_CONST cmp, op2_addr, Z_LVAL_P(Z_ZV(op1_addr))
7186		swap = 1;
7187	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_MODE(op1_addr) != IS_CONST_ZVAL) {
7188		|	LONG_OP_WITH_CONST cmp, op1_addr, Z_LVAL_P(Z_ZV(op2_addr))
7189	} else {
7190		|	GET_ZVAL_LVAL ZREG_R0, op1_addr
7191		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
7192			|	test r0, r0
7193		} else {
7194			|	LONG_OP cmp, ZREG_R0, op2_addr, r0
7195		}
7196	}
7197
7198	if (smart_branch_opcode) {
7199		if (smart_branch_opcode == ZEND_JMPZ_EX ||
7200		    smart_branch_opcode == ZEND_JMPNZ_EX) {
7201
7202			switch (opline->opcode) {
7203				case ZEND_IS_EQUAL:
7204				case ZEND_IS_IDENTICAL:
7205				case ZEND_CASE:
7206				case ZEND_CASE_STRICT:
7207					|	sete al
7208					break;
7209				case ZEND_IS_NOT_EQUAL:
7210				case ZEND_IS_NOT_IDENTICAL:
7211					|	setne al
7212					break;
7213				case ZEND_IS_SMALLER:
7214					if (swap) {
7215						|	setg al
7216					} else {
7217						|	setl al
7218					}
7219					break;
7220				case ZEND_IS_SMALLER_OR_EQUAL:
7221					if (swap) {
7222						|	setge al
7223					} else {
7224						|	setle al
7225					}
7226					break;
7227				default:
7228					ZEND_UNREACHABLE();
7229			}
7230			|	movzx eax, al
7231			|	lea eax, [eax + 2]
7232			|	SET_ZVAL_TYPE_INFO res_addr, eax
7233		}
7234		if (smart_branch_opcode == ZEND_JMPZ ||
7235		    smart_branch_opcode == ZEND_JMPZ_EX) {
7236			switch (opline->opcode) {
7237				case ZEND_IS_EQUAL:
7238				case ZEND_IS_IDENTICAL:
7239				case ZEND_CASE:
7240				case ZEND_CASE_STRICT:
7241					if (exit_addr) {
7242						| jne &exit_addr
7243					} else {
7244						| jne => target_label
7245					}
7246					break;
7247				case ZEND_IS_NOT_EQUAL:
7248					if (exit_addr) {
7249						| je &exit_addr
7250					} else {
7251						| je => target_label
7252					}
7253					break;
7254				case ZEND_IS_NOT_IDENTICAL:
7255					if (exit_addr) {
7256						| jne &exit_addr
7257					} else {
7258						| je => target_label
7259					}
7260					break;
7261				case ZEND_IS_SMALLER:
7262					if (swap) {
7263						if (exit_addr) {
7264							| jle &exit_addr
7265						} else {
7266							| jle => target_label
7267						}
7268					} else {
7269						if (exit_addr) {
7270							| jge &exit_addr
7271						} else {
7272							| jge => target_label
7273						}
7274					}
7275					break;
7276				case ZEND_IS_SMALLER_OR_EQUAL:
7277					if (swap) {
7278						if (exit_addr) {
7279							| jl &exit_addr
7280						} else {
7281							| jl => target_label
7282						}
7283					} else {
7284						if (exit_addr) {
7285							| jg &exit_addr
7286						} else {
7287							| jg => target_label
7288						}
7289					}
7290					break;
7291				default:
7292					ZEND_UNREACHABLE();
7293			}
7294		} else if (smart_branch_opcode == ZEND_JMPNZ ||
7295		           smart_branch_opcode == ZEND_JMPNZ_EX) {
7296			switch (opline->opcode) {
7297				case ZEND_IS_EQUAL:
7298				case ZEND_IS_IDENTICAL:
7299				case ZEND_CASE:
7300				case ZEND_CASE_STRICT:
7301					if (exit_addr) {
7302						| je &exit_addr
7303					} else {
7304						| je => target_label
7305					}
7306					break;
7307				case ZEND_IS_NOT_EQUAL:
7308					if (exit_addr) {
7309						| jne &exit_addr
7310					} else {
7311						| jne => target_label
7312					}
7313					break;
7314				case ZEND_IS_NOT_IDENTICAL:
7315					if (exit_addr) {
7316						| je &exit_addr
7317					} else {
7318						| jne => target_label
7319					}
7320					break;
7321				case ZEND_IS_SMALLER:
7322					if (swap) {
7323						if (exit_addr) {
7324							| jg &exit_addr
7325						} else {
7326							| jg => target_label
7327						}
7328					} else {
7329						if (exit_addr) {
7330							| jl &exit_addr
7331						} else {
7332							| jl => target_label
7333						}
7334					}
7335					break;
7336				case ZEND_IS_SMALLER_OR_EQUAL:
7337					if (swap) {
7338						if (exit_addr) {
7339							| jge &exit_addr
7340						} else {
7341							| jge => target_label
7342						}
7343					} else {
7344						if (exit_addr) {
7345							| jle &exit_addr
7346						} else {
7347							| jle => target_label
7348						}
7349					}
7350					break;
7351				default:
7352					ZEND_UNREACHABLE();
7353			}
7354		} else {
7355			ZEND_UNREACHABLE();
7356		}
7357	} else {
7358		switch (opline->opcode) {
7359			case ZEND_IS_EQUAL:
7360			case ZEND_IS_IDENTICAL:
7361			case ZEND_CASE:
7362			case ZEND_CASE_STRICT:
7363				|	sete al
7364				break;
7365			case ZEND_IS_NOT_EQUAL:
7366			case ZEND_IS_NOT_IDENTICAL:
7367				|	setne al
7368				break;
7369			case ZEND_IS_SMALLER:
7370				if (swap) {
7371					|	setg al
7372				} else {
7373					|	setl al
7374				}
7375				break;
7376			case ZEND_IS_SMALLER_OR_EQUAL:
7377				if (swap) {
7378					|	setge al
7379				} else {
7380					|	setle al
7381				}
7382				break;
7383			default:
7384				ZEND_UNREACHABLE();
7385		}
7386		|	movzx eax, al
7387		|	add eax, 2
7388		|	SET_ZVAL_TYPE_INFO res_addr, eax
7389	}
7390
7391	return 1;
7392}
7393
7394static int zend_jit_cmp_double_common(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, bool swap, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7395{
7396	if (smart_branch_opcode) {
7397		if (smart_branch_opcode == ZEND_JMPZ) {
7398			switch (opline->opcode) {
7399				case ZEND_IS_EQUAL:
7400				case ZEND_IS_IDENTICAL:
7401				case ZEND_CASE:
7402				case ZEND_CASE_STRICT:
7403					if (exit_addr) {
7404						| jne &exit_addr
7405						| jp &exit_addr
7406					} else {
7407						| jne => target_label
7408						| jp => target_label
7409					}
7410					break;
7411				case ZEND_IS_NOT_EQUAL:
7412					| jp >1
7413					if (exit_addr) {
7414						| je &exit_addr
7415					} else {
7416						| je => target_label
7417					}
7418					|1:
7419					break;
7420				case ZEND_IS_NOT_IDENTICAL:
7421					if (exit_addr) {
7422						| jne &exit_addr
7423						| jp &exit_addr
7424					} else {
7425						| jp >1
7426						| je => target_label
7427						|1:
7428					}
7429					break;
7430				case ZEND_IS_SMALLER:
7431					if (swap) {
7432						if (exit_addr) {
7433							| jbe &exit_addr
7434						} else {
7435							| jbe => target_label
7436						}
7437					} else {
7438						if (exit_addr) {
7439							| jae &exit_addr
7440							| jp &exit_addr
7441						} else {
7442							| jae => target_label
7443							| jp => target_label
7444						}
7445					}
7446					break;
7447				case ZEND_IS_SMALLER_OR_EQUAL:
7448					if (swap) {
7449						if (exit_addr) {
7450							| jb &exit_addr
7451						} else {
7452							| jb => target_label
7453						}
7454					} else {
7455						if (exit_addr) {
7456							| ja &exit_addr
7457							| jp &exit_addr
7458						} else {
7459							| ja => target_label
7460							| jp => target_label
7461						}
7462					}
7463					break;
7464				default:
7465					ZEND_UNREACHABLE();
7466			}
7467		} else if (smart_branch_opcode == ZEND_JMPNZ) {
7468			switch (opline->opcode) {
7469				case ZEND_IS_EQUAL:
7470				case ZEND_IS_IDENTICAL:
7471				case ZEND_CASE:
7472				case ZEND_CASE_STRICT:
7473					| jp >1
7474					if (exit_addr) {
7475						| je &exit_addr
7476					} else {
7477						| je => target_label
7478					}
7479					|1:
7480					break;
7481				case ZEND_IS_NOT_EQUAL:
7482					if (exit_addr) {
7483						| jne &exit_addr
7484						| jp &exit_addr
7485					} else {
7486						| jne => target_label
7487						| jp => target_label
7488					}
7489					break;
7490				case ZEND_IS_NOT_IDENTICAL:
7491					if (exit_addr) {
7492						| jp >1
7493						| je &exit_addr
7494						|1:
7495					} else {
7496						| jne => target_label
7497						| jp => target_label
7498					}
7499					break;
7500				case ZEND_IS_SMALLER:
7501					if (swap) {
7502						if (exit_addr) {
7503							| ja &exit_addr
7504						} else {
7505							| ja => target_label
7506						}
7507					} else {
7508						| jp >1
7509						if (exit_addr) {
7510							| jb &exit_addr
7511						} else {
7512							| jb => target_label
7513						}
7514						|1:
7515					}
7516					break;
7517				case ZEND_IS_SMALLER_OR_EQUAL:
7518					if (swap) {
7519						if (exit_addr) {
7520							| jae &exit_addr
7521						} else {
7522							| jae => target_label
7523						}
7524					} else {
7525						| jp >1
7526						if (exit_addr) {
7527							| jbe &exit_addr
7528						} else {
7529							| jbe => target_label
7530						}
7531						|1:
7532					}
7533					break;
7534				default:
7535					ZEND_UNREACHABLE();
7536			}
7537		} else if (smart_branch_opcode == ZEND_JMPZ_EX) {
7538			switch (opline->opcode) {
7539				case ZEND_IS_EQUAL:
7540				case ZEND_IS_IDENTICAL:
7541				case ZEND_CASE:
7542				case ZEND_CASE_STRICT:
7543					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7544					|	jne => target_label
7545					|	jp => target_label
7546					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7547					break;
7548				case ZEND_IS_NOT_EQUAL:
7549				case ZEND_IS_NOT_IDENTICAL:
7550					|	jp >1
7551					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7552					|	je => target_label
7553					|1:
7554					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7555					break;
7556				case ZEND_IS_SMALLER:
7557					if (swap) {
7558						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7559						|	jbe => target_label
7560						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7561					} else {
7562						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7563						|	jae => target_label
7564						|	jp => target_label
7565						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7566					}
7567					break;
7568				case ZEND_IS_SMALLER_OR_EQUAL:
7569					if (swap) {
7570						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7571						|	jb => target_label
7572						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7573					} else {
7574						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7575						|	ja => target_label
7576						|	jp => target_label
7577						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7578					}
7579					break;
7580				default:
7581					ZEND_UNREACHABLE();
7582			}
7583		} else if (smart_branch_opcode == ZEND_JMPNZ_EX) {
7584			switch (opline->opcode) {
7585				case ZEND_IS_EQUAL:
7586				case ZEND_IS_IDENTICAL:
7587				case ZEND_CASE:
7588				case ZEND_CASE_STRICT:
7589					|	jp >1
7590					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7591					|	je => target_label
7592					|1:
7593					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7594					break;
7595				case ZEND_IS_NOT_EQUAL:
7596				case ZEND_IS_NOT_IDENTICAL:
7597					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7598					|	jne => target_label
7599					|	jp => target_label
7600					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7601					break;
7602				case ZEND_IS_SMALLER:
7603					if (swap) {
7604						|	seta al
7605						|	movzx eax, al
7606						|	lea eax, [eax + 2]
7607						|	SET_ZVAL_TYPE_INFO res_addr, eax
7608						|	ja => target_label
7609					} else {
7610						|	jp >1
7611						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7612						|	jb => target_label
7613						|1:
7614						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7615					}
7616					break;
7617				case ZEND_IS_SMALLER_OR_EQUAL:
7618					if (swap) {
7619						|	setae al
7620						|	movzx eax, al
7621						|	lea eax, [eax + 2]
7622						|	SET_ZVAL_TYPE_INFO res_addr, eax
7623						|	jae => target_label
7624					} else {
7625						|	jp >1
7626						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7627						|	jbe => target_label
7628						|1:
7629						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7630					}
7631					break;
7632				default:
7633					ZEND_UNREACHABLE();
7634			}
7635		} else {
7636			ZEND_UNREACHABLE();
7637		}
7638	} else {
7639		switch (opline->opcode) {
7640			case ZEND_IS_EQUAL:
7641			case ZEND_IS_IDENTICAL:
7642			case ZEND_CASE:
7643			case ZEND_CASE_STRICT:
7644				|	jp >1
7645				|	mov eax, IS_TRUE
7646				|	je >2
7647				|1:
7648				|	mov eax, IS_FALSE
7649				|2:
7650				break;
7651			case ZEND_IS_NOT_EQUAL:
7652			case ZEND_IS_NOT_IDENTICAL:
7653				|	jp >1
7654				|	mov eax, IS_FALSE
7655				|	je >2
7656				|1:
7657				|	mov eax, IS_TRUE
7658				|2:
7659				break;
7660			case ZEND_IS_SMALLER:
7661				if (swap) {
7662					|	seta al
7663					|	movzx eax, al
7664					|	add eax, 2
7665				} else {
7666					|	jp >1
7667					|	mov eax, IS_TRUE
7668					|	jb >2
7669					|1:
7670					|	mov eax, IS_FALSE
7671					|2:
7672				}
7673				break;
7674			case ZEND_IS_SMALLER_OR_EQUAL:
7675				if (swap) {
7676					|	setae al
7677					|	movzx eax, al
7678					|	add eax, 2
7679				} else {
7680					|	jp >1
7681					|	mov eax, IS_TRUE
7682					|	jbe >2
7683					|1:
7684					|	mov eax, IS_FALSE
7685					|2:
7686				}
7687				break;
7688			default:
7689				ZEND_UNREACHABLE();
7690		}
7691		|	SET_ZVAL_TYPE_INFO res_addr, eax
7692	}
7693
7694	return 1;
7695}
7696
7697static 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, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7698{
7699	zend_reg tmp_reg = ZREG_XMM0;
7700
7701	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op1_addr, ZREG_R0
7702	|	DOUBLE_CMP tmp_reg, op2_addr
7703
7704	return zend_jit_cmp_double_common(Dst, opline, res_addr, 0, smart_branch_opcode, target_label, target_label2, exit_addr);
7705}
7706
7707static 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, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7708{
7709	zend_reg tmp_reg = ZREG_XMM0;
7710
7711	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, ZREG_R0
7712	|	DOUBLE_CMP tmp_reg, op1_addr
7713
7714	return zend_jit_cmp_double_common(Dst, opline, res_addr, /* swap */ 1, smart_branch_opcode, target_label, target_label2, exit_addr);
7715}
7716
7717static 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, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7718{
7719	bool swap = 0;
7720
7721	if (Z_MODE(op1_addr) == IS_REG) {
7722		|	DOUBLE_CMP Z_REG(op1_addr), op2_addr
7723	} else if (Z_MODE(op2_addr) == IS_REG) {
7724		|	DOUBLE_CMP Z_REG(op2_addr), op1_addr
7725		swap = 1;
7726	} else {
7727		zend_reg tmp_reg = ZREG_XMM0;
7728
7729		|	DOUBLE_GET_ZVAL_DVAL tmp_reg, op1_addr
7730		|	DOUBLE_CMP tmp_reg, op2_addr
7731	}
7732
7733	return zend_jit_cmp_double_common(Dst, opline, res_addr, swap, smart_branch_opcode, target_label, target_label2, exit_addr);
7734}
7735
7736static int zend_jit_cmp_slow(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7737{
7738	|	test, eax, eax
7739	if (smart_branch_opcode) {
7740		if (smart_branch_opcode == ZEND_JMPZ_EX ||
7741		    smart_branch_opcode == ZEND_JMPNZ_EX) {
7742			switch (opline->opcode) {
7743				case ZEND_IS_EQUAL:
7744				case ZEND_CASE:
7745					|	sete al
7746					break;
7747				case ZEND_IS_NOT_EQUAL:
7748					|	setne al
7749					break;
7750				case ZEND_IS_SMALLER:
7751					|	setl al
7752					break;
7753				case ZEND_IS_SMALLER_OR_EQUAL:
7754					|	setle al
7755					break;
7756				default:
7757					ZEND_UNREACHABLE();
7758			}
7759			|	movzx eax, al
7760			|	lea eax, [eax + 2]
7761			|	SET_ZVAL_TYPE_INFO res_addr, eax
7762		}
7763		if (smart_branch_opcode == ZEND_JMPZ ||
7764		    smart_branch_opcode == ZEND_JMPZ_EX) {
7765			switch (opline->opcode) {
7766				case ZEND_IS_EQUAL:
7767				case ZEND_CASE:
7768					if (exit_addr) {
7769						| jne &exit_addr
7770					} else {
7771						| jne => target_label
7772					}
7773					break;
7774				case ZEND_IS_NOT_EQUAL:
7775					if (exit_addr) {
7776						| je &exit_addr
7777					} else {
7778						| je => target_label
7779					}
7780					break;
7781				case ZEND_IS_SMALLER:
7782					if (exit_addr) {
7783						| jge &exit_addr
7784					} else {
7785						| jge => target_label
7786					}
7787					break;
7788				case ZEND_IS_SMALLER_OR_EQUAL:
7789					if (exit_addr) {
7790						| jg &exit_addr
7791					} else {
7792						| jg => target_label
7793					}
7794					break;
7795				default:
7796					ZEND_UNREACHABLE();
7797			}
7798		} else if (smart_branch_opcode == ZEND_JMPNZ ||
7799		           smart_branch_opcode == ZEND_JMPNZ_EX) {
7800			switch (opline->opcode) {
7801				case ZEND_IS_EQUAL:
7802				case ZEND_CASE:
7803					if (exit_addr) {
7804						| je &exit_addr
7805					} else {
7806						| je => target_label
7807					}
7808					break;
7809				case ZEND_IS_NOT_EQUAL:
7810					if (exit_addr) {
7811						| jne &exit_addr
7812					} else {
7813						| jne => target_label
7814					}
7815					break;
7816				case ZEND_IS_SMALLER:
7817					if (exit_addr) {
7818						| jl &exit_addr
7819					} else {
7820						| jl => target_label
7821					}
7822					break;
7823				case ZEND_IS_SMALLER_OR_EQUAL:
7824					if (exit_addr) {
7825						| jle &exit_addr
7826					} else {
7827						| jle => target_label
7828					}
7829					break;
7830				default:
7831					ZEND_UNREACHABLE();
7832			}
7833		} else {
7834			ZEND_UNREACHABLE();
7835		}
7836	} else {
7837		switch (opline->opcode) {
7838			case ZEND_IS_EQUAL:
7839			case ZEND_CASE:
7840				|	sete al
7841				break;
7842			case ZEND_IS_NOT_EQUAL:
7843				|	setne al
7844				break;
7845			case ZEND_IS_SMALLER:
7846				|	setl al
7847				break;
7848			case ZEND_IS_SMALLER_OR_EQUAL:
7849				|	setle al
7850				break;
7851			default:
7852				ZEND_UNREACHABLE();
7853		}
7854		|	movzx eax, al
7855		|	add eax, 2
7856		|	SET_ZVAL_TYPE_INFO res_addr, eax
7857	}
7858
7859	return 1;
7860}
7861
7862static int zend_jit_cmp(dasm_State    **Dst,
7863                        const zend_op  *opline,
7864                        uint32_t        op1_info,
7865                        zend_ssa_range *op1_range,
7866                        zend_jit_addr   op1_addr,
7867                        uint32_t        op2_info,
7868                        zend_ssa_range *op2_range,
7869                        zend_jit_addr   op2_addr,
7870                        zend_jit_addr   res_addr,
7871                        int             may_throw,
7872                        uint8_t      smart_branch_opcode,
7873                        uint32_t        target_label,
7874                        uint32_t        target_label2,
7875                        const void     *exit_addr,
7876                        bool       skip_comparison)
7877{
7878	bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
7879	bool has_slow;
7880
7881	has_slow =
7882		(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
7883		(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
7884		((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
7885		 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))));
7886
7887	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
7888		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
7889			if (op1_info & MAY_BE_DOUBLE) {
7890				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >4
7891			} else {
7892				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9
7893			}
7894		}
7895		if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
7896			if (op2_info & MAY_BE_DOUBLE) {
7897				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3
7898				|.cold_code
7899				|3:
7900				if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
7901					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
7902				}
7903				if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7904					return 0;
7905				}
7906				|	jmp >6
7907				|.code
7908			} else {
7909				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
7910			}
7911		}
7912		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)) {
7913			return 0;
7914		}
7915		if (op1_info & MAY_BE_DOUBLE) {
7916			|.cold_code
7917			|4:
7918			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
7919				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9
7920			}
7921			if (op2_info & MAY_BE_DOUBLE) {
7922				if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
7923					if (!same_ops) {
7924						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >5
7925					} else {
7926						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
7927					}
7928				}
7929				if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7930					return 0;
7931				}
7932				|	jmp >6
7933			}
7934			if (!same_ops) {
7935				|5:
7936				if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
7937					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
7938				}
7939				if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7940					return 0;
7941				}
7942				|	jmp >6
7943			}
7944			|.code
7945		}
7946	} else if ((op1_info & MAY_BE_DOUBLE) &&
7947	           !(op1_info & MAY_BE_LONG) &&
7948	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
7949		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) {
7950			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9
7951		}
7952		if (op2_info & MAY_BE_DOUBLE) {
7953			if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
7954				if (!same_ops && (op2_info & MAY_BE_LONG)) {
7955					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >3
7956				} else {
7957					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
7958				}
7959			}
7960			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7961				return 0;
7962			}
7963		}
7964		if (!same_ops && (op2_info & MAY_BE_LONG)) {
7965			if (op2_info & MAY_BE_DOUBLE) {
7966				|.cold_code
7967			}
7968		    |3:
7969			if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
7970				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
7971			}
7972			if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7973				return 0;
7974			}
7975			if (op2_info & MAY_BE_DOUBLE) {
7976				|	jmp >6
7977				|.code
7978			}
7979		}
7980	} else if ((op2_info & MAY_BE_DOUBLE) &&
7981	           !(op2_info & MAY_BE_LONG) &&
7982	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
7983		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) {
7984			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
7985		}
7986		if (op1_info & MAY_BE_DOUBLE) {
7987			if (!same_ops && (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
7988				if (!same_ops && (op1_info & MAY_BE_LONG)) {
7989					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >3
7990				} else {
7991					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9
7992				}
7993			}
7994			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7995				return 0;
7996			}
7997		}
7998		if (!same_ops && (op1_info & MAY_BE_LONG)) {
7999			if (op1_info & MAY_BE_DOUBLE) {
8000				|.cold_code
8001			}
8002			|3:
8003			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
8004				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9
8005			}
8006			if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8007				return 0;
8008			}
8009			if (op1_info & MAY_BE_DOUBLE) {
8010				|	jmp >6
8011				|.code
8012			}
8013		}
8014	}
8015
8016	if (has_slow ||
8017	    (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
8018	    (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
8019		if (has_slow) {
8020			|.cold_code
8021			|9:
8022		}
8023		|	SET_EX_OPLINE opline, r0
8024		if (Z_MODE(op1_addr) == IS_REG) {
8025			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
8026			if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
8027				return 0;
8028			}
8029			op1_addr = real_addr;
8030		}
8031		if (Z_MODE(op2_addr) == IS_REG) {
8032			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
8033			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
8034				return 0;
8035			}
8036			op2_addr = real_addr;
8037		}
8038		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8039		if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) {
8040			|	IF_NOT_Z_TYPE FCARG1a, IS_UNDEF, >1
8041			|	mov FCARG1a, opline->op1.var
8042			|	EXT_CALL zend_jit_undefined_op_helper, r0
8043			|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
8044			|1:
8045		}
8046		if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) {
8047			|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1
8048			|	mov T1, FCARG1a // save
8049			|	mov FCARG1a, opline->op2.var
8050			|	EXT_CALL zend_jit_undefined_op_helper, r0
8051			|	mov FCARG1a, T1 // restore
8052			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
8053			|	jmp >2
8054			|1:
8055			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8056			|2:
8057		} else {
8058			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8059		}
8060		|	EXT_CALL zend_compare, r0
8061		if ((opline->opcode != ZEND_CASE &&
8062		     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8063		     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
8064		    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8065		     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
8066			|	mov dword T1, eax // save
8067			if (opline->opcode != ZEND_CASE) {
8068				|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, NULL
8069			}
8070			|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL
8071			if (may_throw) {
8072				zend_jit_check_exception_undef_result(Dst, opline);
8073			}
8074			|	mov eax, dword T1 // restore
8075		} else if (may_throw) {
8076#if ZTS
8077			|	mov dword T1, eax // save
8078#else
8079			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(exception)))) {
8080				|	mov dword T1, eax // save
8081			}
8082#endif
8083			zend_jit_check_exception_undef_result(Dst, opline);
8084#if ZTS
8085			|	mov eax, dword T1 // restore
8086#else
8087			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(exception)))) {
8088				|	mov eax, dword T1 // restore
8089			}
8090#endif
8091		}
8092		if (!zend_jit_cmp_slow(Dst, opline, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8093			return 0;
8094		}
8095		if (has_slow) {
8096			|	jmp >6
8097			|.code
8098		}
8099	}
8100
8101	|6:
8102
8103	return 1;
8104}
8105
8106static int zend_jit_identical(dasm_State    **Dst,
8107                              const zend_op  *opline,
8108                              uint32_t        op1_info,
8109                              zend_ssa_range *op1_range,
8110                              zend_jit_addr   op1_addr,
8111                              uint32_t        op2_info,
8112                              zend_ssa_range *op2_range,
8113                              zend_jit_addr   op2_addr,
8114                              zend_jit_addr   res_addr,
8115                              int             may_throw,
8116                              uint8_t      smart_branch_opcode,
8117                              uint32_t        target_label,
8118                              uint32_t        target_label2,
8119                              const void     *exit_addr,
8120                              bool       skip_comparison)
8121{
8122	uint32_t identical_label = (uint32_t)-1;
8123	uint32_t not_identical_label = (uint32_t)-1;
8124
8125	if (smart_branch_opcode && !exit_addr) {
8126		if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8127			if (smart_branch_opcode == ZEND_JMPZ) {
8128				not_identical_label = target_label;
8129			} else if (smart_branch_opcode == ZEND_JMPNZ) {
8130				identical_label = target_label;
8131			} else {
8132				ZEND_UNREACHABLE();
8133			}
8134		} else {
8135			if (smart_branch_opcode == ZEND_JMPZ) {
8136				identical_label = target_label;
8137			} else if (smart_branch_opcode == ZEND_JMPNZ) {
8138				not_identical_label = target_label;
8139			} else {
8140				ZEND_UNREACHABLE();
8141			}
8142		}
8143	}
8144
8145	if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
8146	    (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
8147		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)) {
8148			return 0;
8149		}
8150		return 1;
8151	} else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE &&
8152	           (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) {
8153		if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8154			return 0;
8155		}
8156		return 1;
8157	}
8158
8159	if ((op1_info & MAY_BE_UNDEF) && (op2_info & MAY_BE_UNDEF)) {
8160		op1_info |= MAY_BE_NULL;
8161		op2_info |= MAY_BE_NULL;
8162		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8163		|	IF_Z_TYPE FCARG1a, IS_UNDEF, >1
8164		|.cold_code
8165		|1:
8166		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8167		|	SET_EX_OPLINE opline, r0
8168		|	mov FCARG1d, opline->op1.var
8169		|	EXT_CALL zend_jit_undefined_op_helper, r0
8170		if (may_throw) {
8171			zend_jit_check_exception_undef_result(Dst, opline);
8172		}
8173		|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
8174		|	jmp >1
8175		|.code
8176		|1:
8177		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8178		|	IF_Z_TYPE FCARG2a, IS_UNDEF, >1
8179		|.cold_code
8180		|1:
8181		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8182		|	SET_EX_OPLINE opline, r0
8183		|	mov aword T1, FCARG1a // save
8184		|	mov FCARG1d, opline->op2.var
8185		|	EXT_CALL zend_jit_undefined_op_helper, r0
8186		if (may_throw) {
8187			zend_jit_check_exception_undef_result(Dst, opline);
8188		}
8189		|	mov FCARG1a, aword T1 // restore
8190		|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
8191		|	jmp >1
8192		|.code
8193		|1:
8194	} else if (op1_info & MAY_BE_UNDEF) {
8195		op1_info |= MAY_BE_NULL;
8196		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8197		|	IF_Z_TYPE FCARG1a, IS_UNDEF, >1
8198		|.cold_code
8199		|1:
8200		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8201		|	SET_EX_OPLINE opline, r0
8202		|	mov FCARG1d, opline->op1.var
8203		|	EXT_CALL zend_jit_undefined_op_helper, r0
8204		if (may_throw) {
8205			zend_jit_check_exception_undef_result(Dst, opline);
8206		}
8207		|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
8208		|	jmp >1
8209		|.code
8210		|1:
8211		if (opline->op2_type != IS_CONST) {
8212			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8213		}
8214	} else if (op2_info & MAY_BE_UNDEF) {
8215		op2_info |= MAY_BE_NULL;
8216		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8217		|	IF_Z_TYPE FCARG2a, IS_UNDEF, >1
8218		|.cold_code
8219		|1:
8220		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8221		|	SET_EX_OPLINE opline, r0
8222		|	mov FCARG1d, opline->op2.var
8223		|	EXT_CALL zend_jit_undefined_op_helper, r0
8224		if (may_throw) {
8225			zend_jit_check_exception_undef_result(Dst, opline);
8226		}
8227		|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
8228		|	jmp >1
8229		|.code
8230		|1:
8231		if (opline->op1_type != IS_CONST) {
8232			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8233		}
8234	} else if ((op1_info & op2_info & MAY_BE_ANY) != 0) {
8235		if (opline->op1_type != IS_CONST) {
8236			if (Z_MODE(op1_addr) == IS_REG) {
8237				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
8238				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
8239					return 0;
8240				}
8241				op1_addr = real_addr;
8242			}
8243		}
8244		if (opline->op2_type != IS_CONST) {
8245			if (Z_MODE(op2_addr) == IS_REG) {
8246				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
8247				if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
8248					return 0;
8249				}
8250				op2_addr = real_addr;
8251			}
8252			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8253		}
8254		if (opline->op1_type != IS_CONST) {
8255			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8256		}
8257	}
8258
8259	if ((op1_info & op2_info & MAY_BE_ANY) == 0) {
8260		if ((opline->opcode != ZEND_CASE_STRICT &&
8261		     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8262		     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
8263		    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8264		     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
8265			if (opline->opcode != ZEND_CASE_STRICT) {
8266				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8267			}
8268			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8269		}
8270		if (smart_branch_opcode) {
8271			if (may_throw) {
8272				zend_jit_check_exception_undef_result(Dst, opline);
8273			}
8274			if (exit_addr) {
8275				if (smart_branch_opcode == ZEND_JMPZ) {
8276					|	jmp &exit_addr
8277				}
8278			} else if (not_identical_label != (uint32_t)-1) {
8279				|	jmp =>not_identical_label
8280			}
8281		} else {
8282			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE)
8283			if (may_throw) {
8284				zend_jit_check_exception(Dst);
8285			}
8286		}
8287		return 1;
8288	}
8289
8290	if (opline->op1_type & (IS_CV|IS_VAR)) {
8291		|	ZVAL_DEREF FCARG1a, op1_info
8292	}
8293	if (opline->op2_type & (IS_CV|IS_VAR)) {
8294		|	ZVAL_DEREF FCARG2a, op2_info
8295	}
8296
8297	if (has_concrete_type(op1_info)
8298	 && has_concrete_type(op2_info)
8299	 && concrete_type(op1_info) == concrete_type(op2_info)
8300	 && concrete_type(op1_info) <= IS_TRUE) {
8301		if (smart_branch_opcode) {
8302			if (exit_addr) {
8303				if (smart_branch_opcode == ZEND_JMPNZ) {
8304					|	jmp &exit_addr
8305				}
8306			} else if (identical_label != (uint32_t)-1) {
8307				|	jmp =>identical_label
8308			}
8309		} else {
8310			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE)
8311		}
8312	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) == IS_CONST_ZVAL) {
8313		if (zend_is_identical(Z_ZV(op1_addr), Z_ZV(op2_addr))) {
8314			if (smart_branch_opcode) {
8315				if (exit_addr) {
8316					if (smart_branch_opcode == ZEND_JMPNZ) {
8317						|	jmp &exit_addr
8318					}
8319				} else if (identical_label != (uint32_t)-1) {
8320					|	jmp =>identical_label
8321				}
8322			} else {
8323				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE)
8324			}
8325		} else {
8326			if (smart_branch_opcode) {
8327				if (exit_addr) {
8328					if (smart_branch_opcode == ZEND_JMPZ) {
8329						|	jmp &exit_addr
8330					}
8331				} else if (not_identical_label != (uint32_t)-1) {
8332					|	jmp =>not_identical_label
8333				}
8334			} else {
8335				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE)
8336			}
8337		}
8338	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op1_addr)) <= IS_TRUE) {
8339		zval *val = Z_ZV(op1_addr);
8340
8341		|	cmp byte [FCARG2a + offsetof(zval, u1.v.type)], Z_TYPE_P(val)
8342		if (smart_branch_opcode) {
8343			if (opline->op2_type == IS_VAR && (op2_info & MAY_BE_REF)) {
8344				|	jne >8
8345				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8346				if (may_throw) {
8347					zend_jit_check_exception_undef_result(Dst, opline);
8348				}
8349				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8350					|	jmp &exit_addr
8351				} else if (identical_label != (uint32_t)-1) {
8352					|	jmp =>identical_label
8353				} else {
8354					|	jmp >9
8355				}
8356				|8:
8357			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8358				|	je &exit_addr
8359			} else if (identical_label != (uint32_t)-1) {
8360				|	je =>identical_label
8361			} else {
8362				|	je >9
8363			}
8364		} else {
8365			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8366				|	sete al
8367			} else {
8368				|	setne al
8369			}
8370			|	movzx eax, al
8371			|	lea eax, [eax + 2]
8372			|	SET_ZVAL_TYPE_INFO res_addr, eax
8373		}
8374		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8375		    (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
8376			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8377			if (may_throw) {
8378				zend_jit_check_exception_undef_result(Dst, opline);
8379			}
8380		}
8381		if (exit_addr) {
8382			if (smart_branch_opcode == ZEND_JMPZ) {
8383				|	jmp &exit_addr
8384			}
8385		} else if (smart_branch_opcode && not_identical_label != (uint32_t)-1) {
8386			|	jmp =>not_identical_label
8387		}
8388	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op2_addr)) <= IS_TRUE) {
8389		zval *val = Z_ZV(op2_addr);
8390
8391		|	cmp byte [FCARG1a + offsetof(zval, u1.v.type)], Z_TYPE_P(val)
8392		if (smart_branch_opcode) {
8393			if (opline->opcode != ZEND_CASE_STRICT
8394			 && opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) {
8395				|	jne >8
8396				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8397				if (may_throw) {
8398					zend_jit_check_exception_undef_result(Dst, opline);
8399				}
8400				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8401					|	jmp &exit_addr
8402				} else if (identical_label != (uint32_t)-1) {
8403					|	jmp =>identical_label
8404				} else {
8405					|	jmp >9
8406				}
8407				|8:
8408			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8409				|	je &exit_addr
8410			} else if (identical_label != (uint32_t)-1) {
8411				|	je =>identical_label
8412			} else {
8413				|	je >9
8414			}
8415		} else {
8416			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8417				|	sete al
8418			} else {
8419				|	setne al
8420			}
8421			|	movzx eax, al
8422			|	lea eax, [eax + 2]
8423			|	SET_ZVAL_TYPE_INFO res_addr, eax
8424		}
8425		if (opline->opcode != ZEND_CASE_STRICT
8426		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8427		    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
8428			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8429			if (may_throw) {
8430				zend_jit_check_exception_undef_result(Dst, opline);
8431			}
8432		}
8433		if (smart_branch_opcode) {
8434			if (exit_addr) {
8435				if (smart_branch_opcode == ZEND_JMPZ) {
8436					|	jmp &exit_addr
8437				}
8438			} else if (not_identical_label != (uint32_t)-1) {
8439				|	jmp =>not_identical_label
8440			}
8441		}
8442	} else {
8443		if (opline->op1_type == IS_CONST) {
8444			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8445		}
8446		if (opline->op2_type == IS_CONST) {
8447			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8448		}
8449		|	EXT_CALL zend_is_identical, r0
8450			if ((opline->opcode != ZEND_CASE_STRICT &&
8451			     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8452			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
8453			    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8454			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
8455				|	mov aword T1, r0 // save
8456				if (opline->opcode != ZEND_CASE_STRICT) {
8457					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8458				}
8459				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8460				if (may_throw) {
8461					zend_jit_check_exception_undef_result(Dst, opline);
8462				}
8463				|	mov r0, aword T1 // restore
8464			}
8465		if (smart_branch_opcode) {
8466			|	test al, al
8467			if (exit_addr) {
8468				if (smart_branch_opcode == ZEND_JMPNZ) {
8469					|	jnz &exit_addr
8470				} else {
8471					|	jz &exit_addr
8472				}
8473			} else if (not_identical_label != (uint32_t)-1) {
8474				|	jz =>not_identical_label
8475				if (identical_label != (uint32_t)-1) {
8476					|	jmp =>identical_label
8477				}
8478			} else if (identical_label != (uint32_t)-1) {
8479				|	jnz =>identical_label
8480			}
8481		} else {
8482			|	movzx eax, al
8483			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8484				|	lea eax, [eax + 2]
8485			} else {
8486				|	neg eax
8487				|	lea eax, [eax + 3]
8488			}
8489			|	SET_ZVAL_TYPE_INFO res_addr, eax
8490		}
8491	}
8492
8493	|9:
8494	if (may_throw) {
8495		zend_jit_check_exception(Dst);
8496	}
8497	return 1;
8498}
8499
8500static 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, uint8_t branch_opcode, const void *exit_addr)
8501{
8502	uint32_t true_label = -1;
8503	uint32_t false_label = -1;
8504	bool set_bool = 0;
8505	bool set_bool_not = 0;
8506	bool set_delayed = 0;
8507	bool jmp_done = 0;
8508
8509	if (branch_opcode == ZEND_BOOL) {
8510		set_bool = 1;
8511	} else if (branch_opcode == ZEND_BOOL_NOT) {
8512		set_bool = 1;
8513		set_bool_not = 1;
8514	} else if (branch_opcode == ZEND_JMPZ) {
8515		false_label = target_label;
8516	} else if (branch_opcode == ZEND_JMPNZ) {
8517		true_label = target_label;
8518	} else if (branch_opcode == ZEND_JMPZ_EX) {
8519		set_bool = 1;
8520		false_label = target_label;
8521	} else if (branch_opcode == ZEND_JMPNZ_EX) {
8522		set_bool = 1;
8523		true_label = target_label;
8524	} else {
8525		ZEND_UNREACHABLE();
8526	}
8527
8528	if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
8529		if (zend_is_true(Z_ZV(op1_addr))) {
8530			/* Always TRUE */
8531			if (set_bool) {
8532				if (set_bool_not) {
8533					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8534				} else {
8535					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8536				}
8537			}
8538			if (true_label != (uint32_t)-1) {
8539				|	jmp =>true_label;
8540			}
8541		} else {
8542			/* Always FALSE */
8543			if (set_bool) {
8544				if (set_bool_not) {
8545					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8546				} else {
8547					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8548				}
8549			}
8550			if (false_label != (uint32_t)-1) {
8551				|	jmp =>false_label;
8552			}
8553		}
8554		return 1;
8555	}
8556
8557	if (opline->op1_type == IS_CV && (op1_info & MAY_BE_REF)) {
8558		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8559		|	ZVAL_DEREF FCARG1a, op1_info
8560		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
8561	}
8562
8563	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE)) {
8564		if (!(op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_TRUE))) {
8565			/* Always TRUE */
8566			if (set_bool) {
8567				if (set_bool_not) {
8568					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8569				} else {
8570					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8571				}
8572			}
8573			if (true_label != (uint32_t)-1) {
8574				|	jmp =>true_label;
8575			}
8576		} else {
8577			if (!(op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE)))) {
8578				/* Always FALSE */
8579				if (set_bool) {
8580					if (set_bool_not) {
8581						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8582					} else {
8583						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8584					}
8585				}
8586			} else {
8587				|	CMP_ZVAL_TYPE op1_addr, IS_TRUE
8588				if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
8589				    if ((op1_info & MAY_BE_LONG) &&
8590				        !(op1_info & MAY_BE_UNDEF) &&
8591				        !set_bool) {
8592						if (exit_addr) {
8593							if (branch_opcode == ZEND_JMPNZ) {
8594								|	jl >9
8595							} else {
8596								|	jl &exit_addr
8597							}
8598						} else if (false_label != (uint32_t)-1) {
8599							|	jl =>false_label
8600						} else {
8601							|	jl >9
8602						}
8603						jmp_done = 1;
8604					} else {
8605						|	jg >2
8606					}
8607				}
8608				if (!(op1_info & MAY_BE_TRUE)) {
8609					/* It's FALSE */
8610					if (set_bool) {
8611						if (set_bool_not) {
8612							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8613						} else {
8614							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8615						}
8616					}
8617				} else {
8618					if (exit_addr) {
8619						if (set_bool) {
8620							|	jne >1
8621							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8622							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8623								|	jmp &exit_addr
8624							} else {
8625								|	jmp >9
8626							}
8627							|1:
8628							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8629							if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
8630								if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8631									|	jne &exit_addr
8632								}
8633							}
8634						} else {
8635							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8636								|	je &exit_addr
8637							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8638								|	jne &exit_addr
8639							} else {
8640								|	je >9
8641							}
8642						}
8643					} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8644						if (set_bool) {
8645							|	jne >1
8646							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8647							if (true_label != (uint32_t)-1) {
8648								|	jmp =>true_label
8649							} else {
8650								|	jmp >9
8651							}
8652							|1:
8653							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8654						} else {
8655							if (true_label != (uint32_t)-1) {
8656								|	je =>true_label
8657							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8658								|	jne =>false_label
8659								jmp_done = 1;
8660							} else {
8661								|	je >9
8662							}
8663						}
8664					} else if (set_bool) {
8665						|	sete al
8666						|	movzx eax, al
8667						if (set_bool_not) {
8668							|	neg eax
8669							|	add eax, 3
8670						} else {
8671							|	add eax, 2
8672						}
8673						if ((op1_info & MAY_BE_UNDEF) && (op1_info & MAY_BE_ANY)) {
8674							set_delayed = 1;
8675						} else {
8676							|	SET_ZVAL_TYPE_INFO res_addr, eax
8677						}
8678					}
8679				}
8680			}
8681
8682			/* It's FALSE, but may be UNDEF */
8683			if (op1_info & MAY_BE_UNDEF) {
8684				if (op1_info & MAY_BE_ANY) {
8685					if (set_delayed) {
8686						|	CMP_ZVAL_TYPE op1_addr, IS_UNDEF
8687						|	SET_ZVAL_TYPE_INFO res_addr, eax
8688						|	jz >1
8689					} else {
8690						|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1
8691					}
8692					|.cold_code
8693					|1:
8694				}
8695				|	mov FCARG1d, opline->op1.var
8696				|	SET_EX_OPLINE opline, r0
8697				|	EXT_CALL zend_jit_undefined_op_helper, r0
8698
8699				if (may_throw) {
8700					if (!zend_jit_check_exception_undef_result(Dst, opline)) {
8701						return 0;
8702					}
8703				}
8704
8705				if (exit_addr) {
8706					if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
8707						|	jmp &exit_addr
8708					}
8709				} else if (false_label != (uint32_t)-1) {
8710					|	jmp =>false_label
8711				}
8712				if (op1_info & MAY_BE_ANY) {
8713					if (exit_addr) {
8714						if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8715							|	jmp >9
8716						}
8717					} else if (false_label == (uint32_t)-1) {
8718						|	jmp >9
8719					}
8720					|.code
8721				}
8722			}
8723
8724			if (!jmp_done) {
8725				if (exit_addr) {
8726					if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8727						if (op1_info & MAY_BE_LONG) {
8728							|	jmp >9
8729						}
8730					} else if (op1_info & MAY_BE_LONG) {
8731						|	jmp &exit_addr
8732					}
8733				} else if (false_label != (uint32_t)-1) {
8734					|	jmp =>false_label
8735				} else if ((op1_info & MAY_BE_LONG) || (op1_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
8736					|	jmp >9
8737				}
8738			}
8739		}
8740	}
8741
8742	if (op1_info & MAY_BE_LONG) {
8743		|2:
8744		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
8745			|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2
8746		}
8747		if (Z_MODE(op1_addr) == IS_REG) {
8748			|	test Ra(Z_REG(op1_addr)), Ra(Z_REG(op1_addr))
8749		} else {
8750			|	LONG_OP_WITH_CONST, cmp, op1_addr, Z_L(0)
8751		}
8752		if (set_bool) {
8753			|	setne al
8754			|	movzx eax, al
8755			if (set_bool_not) {
8756				|	neg eax
8757				|	add eax, 3
8758			} else {
8759				|	lea eax, [eax + 2]
8760			}
8761			|	SET_ZVAL_TYPE_INFO res_addr, eax
8762		}
8763		if (exit_addr) {
8764			if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8765				|	jne &exit_addr
8766			} else {
8767				|	je &exit_addr
8768			}
8769		} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8770			if (true_label != (uint32_t)-1) {
8771				|	jne =>true_label
8772				if (false_label != (uint32_t)-1) {
8773					|	jmp =>false_label
8774				}
8775			} else {
8776				|	je =>false_label
8777			}
8778		}
8779	}
8780
8781	if ((op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) == MAY_BE_DOUBLE) {
8782		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8783			|.cold_code
8784		}
8785		|2:
8786		if (CAN_USE_AVX()) {
8787			|	vxorps xmm0, xmm0, xmm0
8788		} else {
8789			|	xorps xmm0, xmm0
8790		}
8791		|	DOUBLE_CMP ZREG_XMM0, op1_addr
8792
8793		if (set_bool) {
8794			if (exit_addr) {
8795				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8796					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8797					|	jp &exit_addr
8798					|	jne &exit_addr
8799					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8800				} else {
8801					|	jp >1
8802					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8803					|	je &exit_addr
8804					|1:
8805					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8806				}
8807			} else if (false_label != (uint32_t)-1) { // JMPZ_EX (p=>true, z=>false, false=>jmp)
8808				|	jp  >1
8809				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8810				|	je  => false_label
8811				|1:
8812				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8813			} else if (true_label != (uint32_t)-1) { // JMPNZ_EX (p=>true, z=>false, true=>jmp)
8814				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8815				|	jp  => true_label
8816				|	jne  => true_label
8817				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8818			} else if (set_bool_not) { // BOOL_NOT (p=>false, z=>true)
8819				|	mov eax, IS_FALSE
8820				|	jp >1
8821				|	jne >1
8822				|	mov eax, IS_TRUE
8823				|1:
8824				|	SET_ZVAL_TYPE_INFO res_addr, eax
8825			} else { // BOOL (p=>true, z=>false)
8826				|	mov eax, IS_TRUE
8827				|	jp >1
8828				|	jne >1
8829				|	mov eax, IS_FALSE
8830				|1:
8831				|	SET_ZVAL_TYPE_INFO res_addr, eax
8832			}
8833			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8834				|	jmp >9
8835				|.code
8836			}
8837		} else {
8838			if (exit_addr) {
8839				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8840					|	jp &exit_addr
8841					|	jne &exit_addr
8842					|1:
8843				} else {
8844					|	jp >1
8845					|	je &exit_addr
8846					|1:
8847				}
8848				if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8849					|	jmp >9
8850				}
8851			} else {
8852				ZEND_ASSERT(true_label != (uint32_t)-1 || false_label != (uint32_t)-1);
8853				if (false_label != (uint32_t)-1 ) {
8854					|	jp  >1
8855					|	je  => false_label
8856					|1:
8857					if (true_label != (uint32_t)-1) {
8858						|	jmp =>true_label
8859					} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8860						|	jmp >9
8861					}
8862				} else {
8863					|	jp  => true_label
8864					|	jne  => true_label
8865					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8866						|	jmp >9
8867					}
8868				}
8869			}
8870			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8871				|.code
8872			}
8873		}
8874	} else if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
8875		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8876			|.cold_code
8877			|2:
8878		}
8879		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
8880			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8881		}
8882		|	SET_EX_OPLINE opline, r0
8883		|	EXT_CALL zend_is_true, r0
8884
8885		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8886			(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
8887			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
8888
8889			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
8890				|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >3
8891			}
8892			|	GET_ZVAL_PTR FCARG1a, op1_addr
8893			|	GC_DELREF FCARG1a
8894			|	jnz >3
8895			|	mov aword T1, r0 // save
8896			|	ZVAL_DTOR_FUNC op1_info, opline
8897			|	mov r0, aword T1 // restore
8898			|3:
8899		}
8900		if (may_throw) {
8901			|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r1
8902			|	jne ->exception_handler_undef
8903		}
8904
8905		if (set_bool) {
8906			if (set_bool_not) {
8907				|	neg eax
8908				|	add eax, 3
8909			} else {
8910				|	add eax, 2
8911			}
8912			|	SET_ZVAL_TYPE_INFO res_addr, eax
8913			if (exit_addr) {
8914				|	CMP_ZVAL_TYPE res_addr, IS_FALSE
8915				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8916					|	jne &exit_addr
8917				} else {
8918					|	je &exit_addr
8919				}
8920			} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8921				|	CMP_ZVAL_TYPE res_addr, IS_FALSE
8922				if (true_label != (uint32_t)-1) {
8923					|	jne =>true_label
8924					if (false_label != (uint32_t)-1) {
8925						|	jmp =>false_label
8926					} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8927						|	jmp >9
8928					}
8929				} else {
8930					|	je =>false_label
8931				}
8932			}
8933			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8934				|	jmp >9
8935				|.code
8936			}
8937		} else {
8938			|	test r0, r0
8939			if (exit_addr) {
8940				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8941					|	jne &exit_addr
8942					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8943						|	jmp >9
8944					}
8945				} else {
8946					|	je &exit_addr
8947					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8948						|	jmp >9
8949					}
8950				}
8951			} else if (true_label != (uint32_t)-1) {
8952				|	jne =>true_label
8953				if (false_label != (uint32_t)-1) {
8954					|	jmp =>false_label
8955				} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8956					|	jmp >9
8957				}
8958			} else {
8959				|	je =>false_label
8960				if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8961					|	jmp >9
8962				}
8963			}
8964
8965			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8966				|.code
8967			}
8968		}
8969	}
8970
8971	|9:
8972
8973	return 1;
8974}
8975
8976static 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)
8977{
8978	if (op1_addr != op1_def_addr) {
8979		if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
8980			return 0;
8981		}
8982		if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
8983			op1_addr = op1_def_addr;
8984		}
8985	}
8986
8987	if (!zend_jit_simple_assign(Dst, opline, res_addr, res_use_info, res_info, opline->op1_type, op1_addr, op1_info, 0, 0, 0, 1)) {
8988		return 0;
8989	}
8990	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
8991		return 0;
8992	}
8993	if (op1_info & MAY_BE_UNDEF) {
8994		zend_jit_check_exception(Dst);
8995	}
8996	return 1;
8997}
8998
8999static 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)
9000{
9001	ZEND_ASSERT(opline->op1_type == IS_CV);
9002
9003	if (op2_addr != op2_def_addr) {
9004		if (!zend_jit_update_regs(Dst, opline->op2.var, op2_addr, op2_def_addr, op2_info)) {
9005			return 0;
9006		}
9007		if (Z_MODE(op2_def_addr) == IS_REG && Z_MODE(op2_addr) != IS_REG) {
9008			op2_addr = op2_def_addr;
9009		}
9010	}
9011
9012	if (Z_MODE(op1_addr) != IS_REG
9013	 && Z_MODE(op1_use_addr) == IS_REG
9014	 && !Z_LOAD(op1_use_addr)
9015	 && !Z_STORE(op1_use_addr)) {
9016		/* Force type update */
9017		op1_info |= MAY_BE_UNDEF;
9018	}
9019	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,
9020			may_throw)) {
9021		return 0;
9022	}
9023	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_addr, op1_def_info, op1_use_addr, op1_info)) {
9024		return 0;
9025	}
9026	if (opline->result_type != IS_UNUSED) {
9027		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
9028			return 0;
9029		}
9030	}
9031
9032	return 1;
9033}
9034
9035/* copy of hidden zend_closure */
9036typedef struct _zend_closure {
9037	zend_object       std;
9038	zend_function     func;
9039	zval              this_ptr;
9040	zend_class_entry *called_scope;
9041	zif_handler       orig_internal_handler;
9042} zend_closure;
9043
9044static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_t used_stack)
9045{
9046	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9047	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9048
9049	if (!exit_addr) {
9050		return 0;
9051	}
9052
9053	|	// Check Stack Overflow
9054	|	MEM_LOAD_ZTS r1, aword, executor_globals, vm_stack_end, r0
9055	|	MEM_LOAD_OP_ZTS sub, r1, aword, executor_globals, vm_stack_top, r0
9056	|	cmp r1, used_stack
9057	|	jb &exit_addr
9058
9059	return 1;
9060}
9061
9062static 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 delayed_fetch_this, int checked_stack)
9063{
9064	uint32_t used_stack;
9065	bool stack_check = 1;
9066
9067	if (func) {
9068		used_stack = zend_vm_calc_used_stack(opline->extended_value, func);
9069		if ((int)used_stack <= checked_stack) {
9070			stack_check = 0;
9071		}
9072	} else {
9073		used_stack = (ZEND_CALL_FRAME_SLOT + opline->extended_value + ZEND_OBSERVER_ENABLED) * sizeof(zval);
9074
9075		|	// if (EXPECTED(ZEND_USER_CODE(func->type))) {
9076		if (!is_closure) {
9077			|	test byte [r0 + offsetof(zend_function, type)], 1
9078			|	mov FCARG1a, used_stack
9079			|	jnz >1
9080		} else {
9081			|	mov FCARG1a, used_stack
9082		}
9083		|	// used_stack += (func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args)) * sizeof(zval);
9084		|	mov edx, opline->extended_value
9085		if (!is_closure) {
9086			|	cmp edx, dword [r0 + offsetof(zend_function, op_array.num_args)]
9087			|	cmova edx, dword [r0 + offsetof(zend_function, op_array.num_args)]
9088			|	sub edx, dword [r0 + offsetof(zend_function, op_array.last_var)]
9089			|	sub edx, dword [r0 + offsetof(zend_function, op_array.T)]
9090		} else {
9091			|	cmp edx, dword [r0 + offsetof(zend_closure, func.op_array.num_args)]
9092			|	cmova edx, dword [r0 + offsetof(zend_closure, func.op_array.num_args)]
9093			|	sub edx, dword [r0 + offsetof(zend_closure, func.op_array.last_var)]
9094			|	sub edx, dword [r0 + offsetof(zend_closure, func.op_array.T)]
9095		}
9096		|	shl edx, 4
9097		|.if X64
9098			|	movsxd r2, edx
9099		|.endif
9100		|	sub FCARG1a, r2
9101		|1:
9102	}
9103
9104	zend_jit_start_reuse_ip();
9105
9106	|	// if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
9107	|	MEM_LOAD_ZTS RX, aword, executor_globals, vm_stack_top, RX
9108
9109	if (stack_check) {
9110		|	// Check Stack Overflow
9111		|	MEM_LOAD_ZTS r2, aword, executor_globals, vm_stack_end, r2
9112		|	sub r2, RX
9113		if (func) {
9114			|	cmp r2, used_stack
9115		} else {
9116			|	cmp r2, FCARG1a
9117		}
9118
9119		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9120			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9121			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9122
9123			if (!exit_addr) {
9124				return 0;
9125			}
9126
9127			|	jb &exit_addr
9128		} else {
9129			|	jb >1
9130			|	// EG(vm_stack_top) = (zval*)((char*)call + used_stack);
9131			|.cold_code
9132			|1:
9133			if (func) {
9134				|	mov FCARG1d, used_stack
9135			}
9136#ifdef _WIN32
9137			if (0) {
9138#else
9139			if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
9140#endif
9141				|	SET_EX_OPLINE opline, r0
9142				|	EXT_CALL zend_jit_int_extend_stack_helper, r0
9143			} else {
9144				if (!is_closure) {
9145					if (func
9146					 && op_array == &func->op_array
9147					 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
9148					 && (sizeof(void*) != 8 || IS_SIGNED_32BIT(func))) {
9149						|	LOAD_ADDR FCARG2a, func
9150					} else {
9151						|	mov FCARG2a, r0
9152					}
9153				} else {
9154					|	lea FCARG2a, aword [r0 + offsetof(zend_closure, func)]
9155				}
9156				|	SET_EX_OPLINE opline, r0
9157				|	EXT_CALL zend_jit_extend_stack_helper, r0
9158			}
9159			|	mov RX, r0
9160			|	jmp >1
9161			|.code
9162		}
9163	}
9164
9165	if (func) {
9166		|	MEM_UPDATE_ZTS add, aword, executor_globals, vm_stack_top, used_stack, r2
9167	} else {
9168		|	MEM_UPDATE_ZTS add, aword, executor_globals, vm_stack_top, FCARG1a, r2
9169	}
9170	|	// zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
9171	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) {
9172		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
9173		|	mov dword EX:RX->This.u1.type_info, (IS_UNDEF | ZEND_CALL_NESTED_FUNCTION)
9174	}
9175#ifdef _WIN32
9176	if (0) {
9177#else
9178	if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
9179#endif
9180		|	// call->func = func;
9181		|1:
9182		|	ADDR_STORE aword EX:RX->func, func, r1
9183	} else {
9184		if (!is_closure) {
9185			|	// call->func = func;
9186			if (func
9187			 && op_array == &func->op_array
9188			 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
9189			 && (sizeof(void*) != 8 || IS_SIGNED_32BIT(func))) {
9190				|	ADDR_STORE aword EX:RX->func, func, r1
9191			} else {
9192				|	mov aword EX:RX->func, r0
9193			}
9194		} else {
9195			|	// call->func = &closure->func;
9196			|	lea r1, aword [r0 + offsetof(zend_closure, func)]
9197			|	mov aword EX:RX->func, r1
9198		}
9199		|1:
9200	}
9201	if (opline->opcode == ZEND_INIT_METHOD_CALL) {
9202		|	// Z_PTR(call->This) = obj;
9203		|	mov r1, aword T1
9204		|	mov aword EX:RX->This.value.ptr, r1
9205	    if (opline->op1_type == IS_UNUSED || delayed_fetch_this) {
9206			|	// call->call_info |= ZEND_CALL_HAS_THIS;
9207			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9208				|	mov dword EX:RX->This.u1.type_info, ZEND_CALL_HAS_THIS
9209			} else {
9210				|	or dword EX:RX->This.u1.type_info, ZEND_CALL_HAS_THIS
9211			}
9212	    } else {
9213			if (opline->op1_type == IS_CV) {
9214				|	// GC_ADDREF(obj);
9215				|	add dword [r1], 1
9216			}
9217			|	// call->call_info |= ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS;
9218			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9219				|	mov dword EX:RX->This.u1.type_info, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS)
9220			} else {
9221				|	or dword EX:RX->This.u1.type_info, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS)
9222			}
9223	    }
9224	} else if (!is_closure) {
9225		|	// Z_CE(call->This) = called_scope;
9226		|	mov aword EX:RX->This.value.ptr, 0
9227	} else {
9228		if (opline->op2_type == IS_CV) {
9229			|	// GC_ADDREF(closure);
9230			|	add dword [r0], 1
9231		}
9232		|	//	object_or_called_scope = closure->called_scope;
9233		|	mov r1, aword [r0 + offsetof(zend_closure, called_scope)]
9234		|	mov aword EX:RX->This.value.ptr, r1
9235		|	// call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE |
9236		|	//	(closure->func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE);
9237		|	mov edx, dword [r0 + offsetof(zend_closure, func.common.fn_flags)]
9238		|	and edx, ZEND_ACC_FAKE_CLOSURE
9239		|	or edx, (ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE)
9240		|	//	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
9241		|	cmp byte [r0 + offsetof(zend_closure, this_ptr.u1.v.type)], IS_UNDEF
9242		|	jz >1
9243		|	//	call_info |= ZEND_CALL_HAS_THIS;
9244		|	or edx, ZEND_CALL_HAS_THIS
9245		|	//	object_or_called_scope = Z_OBJ(closure->this_ptr);
9246		|	mov r1, aword [r0 + offsetof(zend_closure, this_ptr.value.ptr)]
9247	    |1:
9248		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
9249		|	or dword EX:RX->This.u1.type_info, edx
9250		|	// Z_PTR(call->This) = object_or_called_scope;
9251		|	mov aword EX:RX->This.value.ptr, r1
9252		|	cmp aword [r0 + offsetof(zend_closure, func.op_array.run_time_cache__ptr)], 0
9253		|	jnz >1
9254		|	lea FCARG1a, aword [r0 + offsetof(zend_closure, func)]
9255		|	EXT_CALL zend_jit_init_func_run_time_cache_helper, r0
9256		|1:
9257	}
9258	|	// ZEND_CALL_NUM_ARGS(call) = num_args;
9259	|	mov dword EX:RX->This.u2.num_args, opline->extended_value
9260	return 1;
9261}
9262
9263static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zend_function *func, const zend_op *to_opline)
9264{
9265	int32_t exit_point;
9266	const void *exit_addr;
9267
9268	if (func->type == ZEND_INTERNAL_FUNCTION) {
9269#ifdef ZEND_WIN32
9270		// TODO: ASLR may cause different addresses in different workers ???
9271		return 0;
9272#endif
9273	} else if (func->type == ZEND_USER_FUNCTION) {
9274		if (!zend_accel_in_shm(func->op_array.opcodes)) {
9275			/* op_array and op_array->opcodes are not persistent. We can't link. */
9276			return 0;
9277		}
9278	} else {
9279		ZEND_UNREACHABLE();
9280		return 0;
9281	}
9282
9283	exit_point = zend_jit_trace_get_exit_point(to_opline, ZEND_JIT_EXIT_POLYMORPHISM);
9284	exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9285	if (!exit_addr) {
9286		return 0;
9287	}
9288
9289	|	// call = EX(call);
9290	|	mov r1, EX->call
9291	while (level > 0) {
9292		|	mov r1, EX:r1->prev_execute_data
9293		level--;
9294	}
9295
9296	if (func->type == ZEND_USER_FUNCTION &&
9297	    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
9298	     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
9299	     !func->common.function_name)) {
9300		const zend_op *opcodes = func->op_array.opcodes;
9301
9302		|	mov r1, aword EX:r1->func
9303		|   .if X64
9304		||		if (!IS_SIGNED_32BIT(opcodes)) {
9305		|			mov64 r2, ((ptrdiff_t)opcodes)
9306		|			cmp aword [r1 + offsetof(zend_op_array, opcodes)], r2
9307		||		} else {
9308		|			cmp aword [r1 + offsetof(zend_op_array, opcodes)], opcodes
9309		||		}
9310		|	.else
9311		|		cmp aword [r1 + offsetof(zend_op_array, opcodes)], opcodes
9312		|	.endif
9313		|	jne &exit_addr
9314	} else {
9315		|   .if X64
9316		||		if (!IS_SIGNED_32BIT(func)) {
9317		|			mov64 r2, ((ptrdiff_t)func)
9318		|			cmp aword EX:r1->func, r2
9319		||		} else {
9320		|			cmp aword EX:r1->func, func
9321		||		}
9322		|	.else
9323		|		cmp aword EX:r1->func, func
9324		|	.endif
9325		|	jne &exit_addr
9326	}
9327
9328	return 1;
9329}
9330
9331static 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, int checked_stack)
9332{
9333	zend_func_info *info = ZEND_FUNC_INFO(op_array);
9334	zend_call_info *call_info = NULL;
9335	zend_function *func = NULL;
9336
9337	if (delayed_call_chain) {
9338		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9339			return 0;
9340		}
9341	}
9342
9343	if (info) {
9344		call_info = info->callee_info;
9345		while (call_info && call_info->caller_init_opline != opline) {
9346			call_info = call_info->next_callee;
9347		}
9348		if (call_info && call_info->callee_func && !call_info->is_prototype) {
9349			func = call_info->callee_func;
9350		}
9351	}
9352
9353	if (!func
9354	 && trace
9355	 && trace->op == ZEND_JIT_TRACE_INIT_CALL) {
9356#ifdef _WIN32
9357		/* ASLR */
9358		if (trace->func->type != ZEND_INTERNAL_FUNCTION) {
9359			func = (zend_function*)trace->func;
9360		}
9361#else
9362		func = (zend_function*)trace->func;
9363#endif
9364	}
9365
9366#ifdef _WIN32
9367	if (0) {
9368#else
9369	if (opline->opcode == ZEND_INIT_FCALL
9370	 && func
9371	 && func->type == ZEND_INTERNAL_FUNCTION) {
9372#endif
9373		/* load constant address later */
9374	} else if (func && op_array == &func->op_array) {
9375		/* recursive call */
9376		if (!(func->op_array.fn_flags & ZEND_ACC_IMMUTABLE) ||
9377		    (sizeof(void*) == 8 && !IS_SIGNED_32BIT(func))) {
9378			|	mov r0, EX->func
9379		}
9380	} else {
9381		|	// if (CACHED_PTR(opline->result.num))
9382		|	mov r2, EX->run_time_cache
9383		|	mov r0, aword [r2 + opline->result.num]
9384		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
9385		 && func
9386		 && (func->common.fn_flags & ZEND_ACC_IMMUTABLE)
9387		 && opline->opcode != ZEND_INIT_FCALL) {
9388			/* Called func may be changed because of recompilation. See ext/opcache/tests/jit/init_fcall_003.phpt */
9389			|   .if X64
9390			||		if (!IS_SIGNED_32BIT(func)) {
9391			|			mov64 r1, ((ptrdiff_t)func)
9392			|			cmp r0, r1
9393			||		} else {
9394			|			cmp r0, func
9395			||		}
9396			|	.else
9397			|		cmp r0, func
9398			|	.endif
9399			|	jnz >1
9400			|.cold_code
9401			|1:
9402		} else {
9403			|	test r0, r0
9404			|	jz >1
9405		}
9406		|.cold_code
9407		|1:
9408		if (opline->opcode == ZEND_INIT_FCALL
9409		 && func
9410		 && func->type == ZEND_USER_FUNCTION
9411		 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) {
9412			|	LOAD_ADDR FCARG1a, func
9413			|	mov aword [r2 + opline->result.num], FCARG1a
9414			|	EXT_CALL zend_jit_init_func_run_time_cache_helper, r0
9415			|	jmp >3
9416		} else {
9417			zval *zv = RT_CONSTANT(opline, opline->op2);
9418
9419			if (opline->opcode == ZEND_INIT_FCALL) {
9420				|	LOAD_ADDR FCARG1a, Z_STR_P(zv);
9421				|	lea FCARG2a, aword [r2 + opline->result.num]
9422				|	EXT_CALL zend_jit_find_func_helper, r0
9423			} else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) {
9424				|	LOAD_ADDR FCARG1a, Z_STR_P(zv + 1);
9425				|	lea FCARG2a, aword [r2 + opline->result.num]
9426				|	EXT_CALL zend_jit_find_func_helper, r0
9427			} else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
9428				|	LOAD_ADDR FCARG1a, zv;
9429				|	lea FCARG2a, aword [r2 + opline->result.num]
9430				|	EXT_CALL zend_jit_find_ns_func_helper, r0
9431			} else {
9432				ZEND_UNREACHABLE();
9433			}
9434			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9435				int32_t exit_point = zend_jit_trace_get_exit_point(opline,
9436					func && (func->common.fn_flags & ZEND_ACC_IMMUTABLE) ? ZEND_JIT_EXIT_INVALIDATE : 0);
9437				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9438
9439				if (!exit_addr) {
9440					return 0;
9441				}
9442				if (!func || opline->opcode == ZEND_INIT_FCALL) {
9443					|	test r0, r0
9444					|	jnz >3
9445				} else if (func->type == ZEND_USER_FUNCTION
9446					 && !(func->common.fn_flags & ZEND_ACC_IMMUTABLE)) {
9447					const zend_op *opcodes = func->op_array.opcodes;
9448
9449					|   .if X64
9450					||		if (!IS_SIGNED_32BIT(opcodes)) {
9451					|			mov64 r1, ((ptrdiff_t)opcodes)
9452					|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], r1
9453					||		} else {
9454					|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9455					||		}
9456					|	.else
9457					|		cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9458					|	.endif
9459					|	jz >3
9460				} else {
9461					|   .if X64
9462					||		if (!IS_SIGNED_32BIT(func)) {
9463					|			mov64 r1, ((ptrdiff_t)func)
9464					|			cmp r0, r1
9465					||		} else {
9466					|			cmp r0, func
9467					||		}
9468					|	.else
9469					|		cmp r0, func
9470					|	.endif
9471					|	jz >3
9472				}
9473				|	jmp &exit_addr
9474			} else {
9475				|	test r0, r0
9476				|	jnz >3
9477				|	// SAVE_OPLINE();
9478				|	SET_EX_OPLINE opline, r0
9479				|	jmp ->undefined_function
9480			}
9481		}
9482		|.code
9483		|3:
9484	}
9485
9486	if (!zend_jit_push_call_frame(Dst, opline, op_array, func, 0, 0, checked_stack)) {
9487		return 0;
9488	}
9489
9490	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9491		if (!zend_jit_save_call_chain(Dst, call_level)) {
9492			return 0;
9493		}
9494	} else {
9495		delayed_call_chain = 1;
9496		delayed_call_level = call_level;
9497	}
9498
9499	return 1;
9500}
9501
9502static int zend_jit_init_method_call(dasm_State          **Dst,
9503                                     const zend_op        *opline,
9504                                     uint32_t              b,
9505                                     const zend_op_array  *op_array,
9506                                     zend_ssa             *ssa,
9507                                     const zend_ssa_op    *ssa_op,
9508                                     int                   call_level,
9509                                     uint32_t              op1_info,
9510                                     zend_jit_addr         op1_addr,
9511                                     zend_class_entry     *ce,
9512                                     bool                  ce_is_instanceof,
9513                                     bool                  on_this,
9514                                     bool                  delayed_fetch_this,
9515                                     zend_class_entry     *trace_ce,
9516                                     zend_jit_trace_rec   *trace,
9517                                     int                   checked_stack,
9518                                     bool                  polymorphic_side_trace)
9519{
9520	zend_func_info *info = ZEND_FUNC_INFO(op_array);
9521	zend_call_info *call_info = NULL;
9522	zend_function *func = NULL;
9523	zval *function_name;
9524
9525	ZEND_ASSERT(opline->op2_type == IS_CONST);
9526	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
9527
9528	function_name = RT_CONSTANT(opline, opline->op2);
9529
9530	if (info) {
9531		call_info = info->callee_info;
9532		while (call_info && call_info->caller_init_opline != opline) {
9533			call_info = call_info->next_callee;
9534		}
9535		if (call_info && call_info->callee_func && !call_info->is_prototype) {
9536			func = call_info->callee_func;
9537		}
9538	}
9539
9540	if (polymorphic_side_trace) {
9541		/* function is passed in r0 from parent_trace */
9542	} else {
9543		if (on_this) {
9544			zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
9545
9546			|	GET_ZVAL_PTR FCARG1a, this_addr
9547		} else {
9548		    if (op1_info & MAY_BE_REF) {
9549				if (opline->op1_type == IS_CV) {
9550					if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
9551						|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
9552					}
9553					|	ZVAL_DEREF FCARG1a, op1_info
9554					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
9555				} else {
9556					/* Hack: Convert reference to regular value to simplify JIT code */
9557					ZEND_ASSERT(Z_REG(op1_addr) == ZREG_FP);
9558					|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
9559					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
9560					|	EXT_CALL zend_jit_unref_helper, r0
9561					|1:
9562				}
9563			}
9564			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
9565				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9566					int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9567					const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9568
9569					if (!exit_addr) {
9570						return 0;
9571					}
9572					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
9573				} else {
9574					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
9575					|.cold_code
9576					|1:
9577					if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
9578						|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
9579					}
9580					|	SET_EX_OPLINE opline, r0
9581					if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9582						|	EXT_CALL zend_jit_invalid_method_call_tmp, r0
9583					} else {
9584						|	EXT_CALL zend_jit_invalid_method_call, r0
9585					}
9586					|	jmp ->exception_handler
9587					|.code
9588				}
9589			}
9590			|	GET_ZVAL_PTR FCARG1a, op1_addr
9591		}
9592
9593		if (delayed_call_chain) {
9594			if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9595				return 0;
9596			}
9597		}
9598
9599		|	mov aword T1, FCARG1a // save
9600
9601		if (func) {
9602			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
9603			|	mov r0, EX->run_time_cache
9604			|	mov r0, aword [r0 + opline->result.num + sizeof(void*)]
9605			|	test r0, r0
9606			|	jz >1
9607		} else {
9608			|	// if (CACHED_PTR(opline->result.num) == obj->ce)) {
9609			|	mov r0, EX->run_time_cache
9610			|	mov r2, aword [r0 + opline->result.num]
9611			|	cmp r2, [FCARG1a + offsetof(zend_object, ce)]
9612			|	jnz >1
9613			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
9614			|	mov r0, aword [r0 + opline->result.num + sizeof(void*)]
9615		}
9616
9617		|.cold_code
9618		|1:
9619		|	LOAD_ADDR FCARG2a, function_name
9620		|.if X64
9621		|	lea CARG3, aword T1
9622		|.else
9623		|	lea r0, aword T1
9624		|	sub r4, 12
9625		|	push r0
9626		|.endif
9627		|	SET_EX_OPLINE opline, r0
9628		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9629			|	EXT_CALL zend_jit_find_method_tmp_helper, r0
9630		} else {
9631			|	EXT_CALL zend_jit_find_method_helper, r0
9632		}
9633		|.if not(X64)
9634		|	add r4, 12
9635		|.endif
9636		|	test r0, r0
9637		|	jnz >2
9638		|	jmp ->exception_handler
9639		|.code
9640		|2:
9641	}
9642
9643	if ((!func || zend_jit_may_be_modified(func, op_array))
9644	 && trace
9645	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
9646	 && trace->func
9647#ifdef _WIN32
9648	 && trace->func->type != ZEND_INTERNAL_FUNCTION
9649#endif
9650	) {
9651		int32_t exit_point;
9652		const void *exit_addr;
9653
9654		exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : ZEND_JIT_EXIT_METHOD_CALL);
9655		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9656		if (!exit_addr) {
9657			return 0;
9658		}
9659
9660		func = (zend_function*)trace->func;
9661
9662		if (func->type == ZEND_USER_FUNCTION &&
9663		    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
9664		     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
9665		     !func->common.function_name)) {
9666			const zend_op *opcodes = func->op_array.opcodes;
9667
9668			|   .if X64
9669			||		if (!IS_SIGNED_32BIT(opcodes)) {
9670			|			mov64 r1, ((ptrdiff_t)opcodes)
9671			|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], r1
9672			||		} else {
9673			|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9674			||		}
9675			|	.else
9676			|		cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9677			|	.endif
9678			|	jne &exit_addr
9679		} else {
9680			|   .if X64
9681			||		if (!IS_SIGNED_32BIT(func)) {
9682			|			mov64 r1, ((ptrdiff_t)func)
9683			|			cmp r0, r1
9684			||		} else {
9685			|			cmp r0, func
9686			||		}
9687			|	.else
9688			|		cmp r0, func
9689			|	.endif
9690			|	jne &exit_addr
9691		}
9692	}
9693
9694	if (!func) {
9695		|	// if (fbc->common.fn_flags & ZEND_ACC_STATIC) {
9696		|	test dword [r0 + offsetof(zend_function, common.fn_flags)], ZEND_ACC_STATIC
9697		|	jnz >1
9698		|.cold_code
9699		|1:
9700	}
9701
9702	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) != 0) {
9703		|	mov FCARG1a, aword T1 // restore
9704		|	mov FCARG2a, r0
9705		|.if X64
9706		|	mov CARG3d, opline->extended_value
9707		|.else
9708		|	sub r4, 12
9709		|	push opline->extended_value
9710		|.endif
9711		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9712			|	EXT_CALL zend_jit_push_static_metod_call_frame_tmp, r0
9713		} else {
9714			|	EXT_CALL zend_jit_push_static_metod_call_frame, r0
9715		}
9716		|.if not(X64)
9717		|	add r4, 12
9718		|.endif
9719		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR) && !delayed_fetch_this)) {
9720			|	test r0, r0
9721			|	jz ->exception_handler
9722		}
9723		|	mov RX, r0
9724	}
9725
9726	if (!func) {
9727		|	jmp >9
9728		|.code
9729	}
9730
9731	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) == 0) {
9732		if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 0, delayed_fetch_this, checked_stack)) {
9733			return 0;
9734		}
9735	}
9736
9737	if (!func) {
9738		|9:
9739	}
9740	zend_jit_start_reuse_ip();
9741
9742	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9743		if (!zend_jit_save_call_chain(Dst, call_level)) {
9744			return 0;
9745		}
9746	} else {
9747		delayed_call_chain = 1;
9748		delayed_call_level = call_level;
9749	}
9750
9751	return 1;
9752}
9753
9754static int zend_jit_init_closure_call(dasm_State          **Dst,
9755                                      const zend_op        *opline,
9756                                      uint32_t              b,
9757                                      const zend_op_array  *op_array,
9758                                      zend_ssa             *ssa,
9759                                      const zend_ssa_op    *ssa_op,
9760                                      int                   call_level,
9761                                      zend_jit_trace_rec   *trace,
9762                                      int                   checked_stack)
9763{
9764	zend_function *func = NULL;
9765	zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
9766
9767	|	GET_ZVAL_PTR r0, op2_addr
9768
9769	if (ssa->var_info[ssa_op->op2_use].ce != zend_ce_closure
9770	 && !(ssa->var_info[ssa_op->op2_use].type & MAY_BE_CLASS_GUARD)) {
9771		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9772		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9773
9774		if (!exit_addr) {
9775			return 0;
9776		}
9777
9778		|.if X64
9779		||	if (!IS_SIGNED_32BIT(zend_ce_closure)) {
9780		|		mov64 FCARG1a, ((ptrdiff_t)zend_ce_closure)
9781		|		cmp aword [r0 + offsetof(zend_object, ce)], FCARG1a
9782		||	} else {
9783		|		cmp aword [r0 + offsetof(zend_object, ce)], zend_ce_closure
9784		||	}
9785		|.else
9786		|	cmp aword [r0 + offsetof(zend_object, ce)], zend_ce_closure
9787		|.endif
9788		|	jne &exit_addr
9789		if (ssa->var_info && ssa_op->op2_use >= 0) {
9790			ssa->var_info[ssa_op->op2_use].type |= MAY_BE_CLASS_GUARD;
9791			ssa->var_info[ssa_op->op2_use].ce = zend_ce_closure;
9792			ssa->var_info[ssa_op->op2_use].is_instanceof = 0;
9793		}
9794	}
9795
9796	if (trace
9797	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
9798	 && trace->func
9799	 && trace->func->type == ZEND_USER_FUNCTION) {
9800		const zend_op *opcodes;
9801		int32_t exit_point;
9802		const void *exit_addr;
9803
9804		func = (zend_function*)trace->func;
9805		opcodes = func->op_array.opcodes;
9806		exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_CLOSURE_CALL);
9807		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9808		if (!exit_addr) {
9809			return 0;
9810		}
9811
9812		|   .if X64
9813		||		if (!IS_SIGNED_32BIT(opcodes)) {
9814		|			mov64 FCARG1a, ((ptrdiff_t)opcodes)
9815		|			cmp aword [r0 + offsetof(zend_closure, func.op_array.opcodes)], FCARG1a
9816		||		} else {
9817		|			cmp aword [r0 + offsetof(zend_closure, func.op_array.opcodes)], opcodes
9818		||		}
9819		|	.else
9820		|		cmp aword [r0 + offsetof(zend_closure, func.op_array.opcodes)], opcodes
9821		|	.endif
9822		|	jne &exit_addr
9823	}
9824
9825	if (delayed_call_chain) {
9826		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9827			return 0;
9828		}
9829	}
9830
9831	if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 1, 0, checked_stack)) {
9832		return 0;
9833	}
9834
9835	if (zend_jit_needs_call_chain(NULL, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9836		if (!zend_jit_save_call_chain(Dst, call_level)) {
9837			return 0;
9838		}
9839	} else {
9840		delayed_call_chain = 1;
9841		delayed_call_level = call_level;
9842	}
9843
9844	if (trace
9845	 && trace->op == ZEND_JIT_TRACE_END
9846	 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
9847		if (!zend_jit_set_valid_ip(Dst, opline + 1)) {
9848			return 0;
9849		}
9850	}
9851
9852	return 1;
9853}
9854
9855static 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)
9856{
9857	zend_func_info *info = ZEND_FUNC_INFO(op_array);
9858	zend_call_info *call_info = NULL;
9859	const zend_function *func = NULL;
9860	uint32_t i;
9861	zend_jit_addr res_addr;
9862	uint32_t call_num_args = 0;
9863	bool unknown_num_args = 0;
9864	const void *exit_addr = NULL;
9865	const zend_op *prev_opline;
9866
9867	if (RETURN_VALUE_USED(opline)) {
9868		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
9869	} else {
9870		/* CPU stack allocated temporary zval */
9871		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R4, TMP_ZVAL_OFFSET);
9872	}
9873
9874	prev_opline = opline - 1;
9875	while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) {
9876		prev_opline--;
9877	}
9878	if (prev_opline->opcode == ZEND_SEND_UNPACK || prev_opline->opcode == ZEND_SEND_ARRAY ||
9879			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
9880		unknown_num_args = 1;
9881	}
9882
9883	if (info) {
9884		call_info = info->callee_info;
9885		while (call_info && call_info->caller_call_opline != opline) {
9886			call_info = call_info->next_callee;
9887		}
9888		if (call_info && call_info->callee_func && !call_info->is_prototype) {
9889			func = call_info->callee_func;
9890		}
9891		if ((op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)
9892		 && JIT_G(current_frame)
9893		 && JIT_G(current_frame)->call
9894		 && !JIT_G(current_frame)->call->func) {
9895			call_info = NULL; func = NULL; /* megamorphic call from trait */
9896		}
9897	}
9898	if (!func) {
9899		/* resolve function at run time */
9900	} else if (func->type == ZEND_USER_FUNCTION) {
9901		ZEND_ASSERT(opline->opcode != ZEND_DO_ICALL);
9902		call_num_args = call_info->num_args;
9903	} else if (func->type == ZEND_INTERNAL_FUNCTION) {
9904		ZEND_ASSERT(opline->opcode != ZEND_DO_UCALL);
9905		call_num_args = call_info->num_args;
9906	} else {
9907		ZEND_UNREACHABLE();
9908	}
9909
9910	if (trace && !func) {
9911		if (trace->op == ZEND_JIT_TRACE_DO_ICALL) {
9912			ZEND_ASSERT(trace->func->type == ZEND_INTERNAL_FUNCTION);
9913#ifndef ZEND_WIN32
9914			// TODO: ASLR may cause different addresses in different workers ???
9915			func = trace->func;
9916			if (JIT_G(current_frame) &&
9917			    JIT_G(current_frame)->call &&
9918			    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
9919				call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
9920			} else {
9921				unknown_num_args = 1;
9922			}
9923#endif
9924		} else if (trace->op == ZEND_JIT_TRACE_ENTER) {
9925			ZEND_ASSERT(trace->func->type == ZEND_USER_FUNCTION);
9926			if (zend_accel_in_shm(trace->func->op_array.opcodes)) {
9927				func = trace->func;
9928				if (JIT_G(current_frame) &&
9929				    JIT_G(current_frame)->call &&
9930				    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
9931					call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
9932				} else {
9933					unknown_num_args = 1;
9934				}
9935			}
9936		}
9937	}
9938
9939	bool may_have_extra_named_params =
9940		opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS &&
9941		(!func || func->common.fn_flags & ZEND_ACC_VARIADIC);
9942
9943	if (!reuse_ip) {
9944		zend_jit_start_reuse_ip();
9945		|	// call = EX(call);
9946		|	mov RX, EX->call
9947	}
9948	zend_jit_stop_reuse_ip();
9949
9950	|	// fbc = call->func;
9951	|	// mov r2, EX:RX->func ???
9952	|	// SAVE_OPLINE();
9953	|	SET_EX_OPLINE opline, r0
9954
9955	if (opline->opcode == ZEND_DO_FCALL) {
9956		if (!func) {
9957			if (trace) {
9958				uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9959
9960				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9961				if (!exit_addr) {
9962					return 0;
9963				}
9964				|	mov r0, EX:RX->func
9965				|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
9966				|	jnz &exit_addr
9967			}
9968		}
9969	}
9970
9971	if (!delayed_call_chain) {
9972		if (call_level == 1) {
9973			|	mov aword EX->call, 0
9974		} else {
9975			|	//EX(call) = call->prev_execute_data;
9976			|	mov r0, EX:RX->prev_execute_data
9977			|	mov EX->call, r0
9978		}
9979	}
9980	delayed_call_chain = 0;
9981
9982	|	//call->prev_execute_data = execute_data;
9983	|	mov EX:RX->prev_execute_data, EX
9984
9985	if (!func) {
9986		|	mov r0, EX:RX->func
9987	}
9988
9989	if (opline->opcode == ZEND_DO_FCALL) {
9990		if (!func) {
9991			if (!trace) {
9992				|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
9993				|	jnz >1
9994				|.cold_code
9995				|1:
9996				if (!GCC_GLOBAL_REGS) {
9997					|	mov FCARG1a, RX
9998				}
9999				|	EXT_CALL zend_jit_deprecated_helper, r0
10000				|	test al, al
10001				|	mov r0, EX:RX->func // reload
10002				|	jne >1
10003				|	jmp ->exception_handler
10004				|.code
10005				|1:
10006			}
10007		} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
10008			if (!GCC_GLOBAL_REGS) {
10009				|	mov FCARG1a, RX
10010			}
10011			|	EXT_CALL zend_jit_deprecated_helper, r0
10012			|	test al, al
10013			|	je ->exception_handler
10014		}
10015	}
10016
10017	if (!func
10018	 && opline->opcode != ZEND_DO_UCALL
10019	 && opline->opcode != ZEND_DO_ICALL) {
10020		|	cmp byte [r0 + offsetof(zend_function, type)], ZEND_USER_FUNCTION
10021		|	jne >8
10022	}
10023
10024	if ((!func || func->type == ZEND_USER_FUNCTION)
10025	 && opline->opcode != ZEND_DO_ICALL) {
10026		|	// EX(call) = NULL;
10027		|	mov aword EX:RX->call, 0
10028
10029		if (RETURN_VALUE_USED(opline)) {
10030			|	// EX(return_value) = EX_VAR(opline->result.var);
10031			|	LOAD_ZVAL_ADDR r2, res_addr
10032			|	mov aword EX:RX->return_value, r2
10033		} else {
10034			|	// EX(return_value) = 0;
10035			|	mov aword EX:RX->return_value, 0
10036		}
10037
10038		//EX_LOAD_RUN_TIME_CACHE(op_array);
10039		if (!func || func->op_array.cache_size) {
10040			if (func && op_array == &func->op_array) {
10041				/* recursive call */
10042				if (trace || func->op_array.cache_size > sizeof(void*)) {
10043					|	mov r2, EX->run_time_cache
10044					|	mov EX:RX->run_time_cache, r2
10045				}
10046			} else {
10047				if (func
10048				 && !(func->op_array.fn_flags & ZEND_ACC_CLOSURE)
10049				 && ZEND_MAP_PTR_IS_OFFSET(func->op_array.run_time_cache)) {
10050					|	MEM_LOAD_ZTS r2, aword, compiler_globals, map_ptr_base, r1
10051					|	mov r2, aword [r2 + (uintptr_t)ZEND_MAP_PTR(func->op_array.run_time_cache)]
10052				} else if ((func && (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) ||
10053						(JIT_G(current_frame) &&
10054						 JIT_G(current_frame)->call &&
10055						 TRACE_FRAME_IS_CLOSURE_CALL(JIT_G(current_frame)->call))) {
10056					/* Closures always use direct pointers */
10057					|	mov r0, EX:RX->func
10058					|	mov r2, aword [r0 + offsetof(zend_op_array, run_time_cache__ptr)]
10059				} else {
10060					if (func) {
10061						|	mov r0, EX:RX->func
10062					}
10063					|	mov r2, aword [r0 + offsetof(zend_op_array, run_time_cache__ptr)]
10064					|	test r2, 1
10065					|	jz >1
10066					|	MEM_LOAD_OP_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1
10067					|	mov r2, aword [r2]
10068					|1:
10069				}
10070				|	mov EX:RX->run_time_cache, r2
10071			}
10072		}
10073
10074		|	// EG(current_execute_data) = execute_data;
10075		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, RX, r1
10076		|	mov FP, RX
10077
10078		|	// opline = op_array->opcodes;
10079		if (func && !unknown_num_args) {
10080
10081			for (i = call_num_args; i < func->op_array.last_var; i++) {
10082				uint32_t n = EX_NUM_TO_VAR(i);
10083				|	SET_Z_TYPE_INFO RX + n, IS_UNDEF
10084			}
10085
10086			if (call_num_args <= func->op_array.num_args) {
10087				if (!trace || (trace->op == ZEND_JIT_TRACE_END
10088				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
10089					uint32_t num_args;
10090
10091					if ((func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
10092						if (trace) {
10093							num_args = 0;
10094						} else if (call_info) {
10095							num_args = skip_valid_arguments(op_array, ssa, call_info);
10096						} else {
10097							num_args = call_num_args;
10098						}
10099					} else {
10100						num_args = call_num_args;
10101					}
10102					if (zend_accel_in_shm(func->op_array.opcodes)) {
10103						|	LOAD_IP_ADDR (func->op_array.opcodes + num_args)
10104					} else {
10105						|	mov r0, EX->func
10106						if (GCC_GLOBAL_REGS) {
10107							|	mov IP, aword [r0 + offsetof(zend_op_array, opcodes)]
10108							if (num_args) {
10109								|	add IP, (num_args * sizeof(zend_op))
10110							}
10111						} else {
10112							|	mov FCARG1a, aword [r0 + offsetof(zend_op_array, opcodes)]
10113							if (num_args) {
10114								|	add FCARG1a, (num_args * sizeof(zend_op))
10115							}
10116							|	mov aword EX->opline, FCARG1a
10117						}
10118					}
10119
10120					if (GCC_GLOBAL_REGS && !trace && op_array == &func->op_array
10121							&& num_args >= op_array->required_num_args) {
10122						/* recursive call */
10123						if (ZEND_OBSERVER_ENABLED) {
10124							|	SAVE_IP
10125							|	mov FCARG1a, FP
10126							|	EXT_CALL zend_observer_fcall_begin, r0
10127						}
10128#ifdef CONTEXT_THREADED_JIT
10129						|	call >1
10130						|.cold_code
10131						|1:
10132						|	pop r0
10133						|	jmp =>num_args
10134						|.code
10135#else
10136						|	jmp =>num_args
10137#endif
10138						return 1;
10139					}
10140				}
10141			} else {
10142				if (!trace || (trace->op == ZEND_JIT_TRACE_END
10143				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
10144					if (func && zend_accel_in_shm(func->op_array.opcodes)) {
10145						|	LOAD_IP_ADDR (func->op_array.opcodes)
10146					} else if (GCC_GLOBAL_REGS) {
10147						|	mov IP, aword [r0 + offsetof(zend_op_array, opcodes)]
10148					} else {
10149						|	mov FCARG1a, aword [r0 + offsetof(zend_op_array, opcodes)]
10150						|	mov aword EX->opline, FCARG1a
10151					}
10152				}
10153				if (!GCC_GLOBAL_REGS) {
10154					|	mov FCARG1a, FP
10155				}
10156				|	EXT_CALL zend_jit_copy_extra_args_helper, r0
10157			}
10158		} else {
10159			|	// opline = op_array->opcodes
10160			if (func && zend_accel_in_shm(func->op_array.opcodes)) {
10161				|	LOAD_IP_ADDR (func->op_array.opcodes)
10162			} else if (GCC_GLOBAL_REGS) {
10163				|	mov IP, aword [r0 + offsetof(zend_op_array, opcodes)]
10164			} else {
10165				|	mov FCARG1a, aword [r0 + offsetof(zend_op_array, opcodes)]
10166				|	mov aword EX->opline, FCARG1a
10167			}
10168			if (func) {
10169				|	// num_args = EX_NUM_ARGS();
10170				|	mov ecx, dword [FP + offsetof(zend_execute_data, This.u2.num_args)]
10171				|	// if (UNEXPECTED(num_args > first_extra_arg))
10172				|	cmp ecx, (func->op_array.num_args)
10173			} else {
10174				|	// first_extra_arg = op_array->num_args;
10175				|	mov edx, dword [r0 + offsetof(zend_op_array, num_args)]
10176				|	// num_args = EX_NUM_ARGS();
10177				|	mov ecx, dword [FP + offsetof(zend_execute_data, This.u2.num_args)]
10178				|	// if (UNEXPECTED(num_args > first_extra_arg))
10179				|	cmp ecx, edx
10180			}
10181			|	jg >1
10182			|.cold_code
10183			|1:
10184			if (!GCC_GLOBAL_REGS) {
10185				|	mov FCARG1a, FP
10186			}
10187			|	EXT_CALL zend_jit_copy_extra_args_helper, r0
10188			if (!func) {
10189				|	mov r0, EX->func // reload
10190			}
10191			|	mov ecx, dword [FP + offsetof(zend_execute_data, This.u2.num_args)] // reload
10192			|	jmp >1
10193			|.code
10194			if (!func || (func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0) {
10195				if (!func) {
10196					|	// if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0))
10197					|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_HAS_TYPE_HINTS
10198					|	jnz >1
10199				}
10200				|	// opline += num_args;
10201				|.if X64
10202					||	ZEND_ASSERT(sizeof(zend_op) == 32);
10203					|	mov edx, ecx
10204					|	shl r2, 5
10205				|.else
10206					|	imul r2, ecx, sizeof(zend_op)
10207				|.endif
10208				|	ADD_IP r2
10209			}
10210			|1:
10211			|	// if (EXPECTED((int)num_args < op_array->last_var)) {
10212			if (func) {
10213				|	mov edx, (func->op_array.last_var)
10214			} else {
10215				|	mov edx, dword [r0 + offsetof(zend_op_array, last_var)]
10216			}
10217			|	sub edx, ecx
10218			|	jle >3 //???
10219			|	// zval *var = EX_VAR_NUM(num_args);
10220//			|.if X64
10221//				|	movsxd r1, ecx
10222//			|.endif
10223			|	shl r1, 4
10224			|	lea r1, [FP + r1 + (ZEND_CALL_FRAME_SLOT * sizeof(zval))]
10225			|2:
10226			|	SET_Z_TYPE_INFO r1, IS_UNDEF
10227			|	sub edx, 1
10228			|	lea r1, [r1 + 16]
10229			|	jne <2
10230			|3:
10231		}
10232
10233		if (ZEND_OBSERVER_ENABLED) {
10234			if (trace && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
10235				ZEND_ASSERT(trace[1].op == ZEND_JIT_TRACE_VM || trace[1].op == ZEND_JIT_TRACE_END);
10236				|	SET_EX_OPLINE trace[1].opline, r0
10237			} else {
10238				|	SAVE_IP
10239			}
10240			|	mov FCARG1a, FP
10241			|	EXT_CALL zend_observer_fcall_begin, r0
10242		}
10243
10244		if (trace) {
10245			if (!func && (opline->opcode != ZEND_DO_UCALL)) {
10246				|	jmp >9
10247			}
10248		} else {
10249#ifdef CONTEXT_THREADED_JIT
10250			|	call ->context_threaded_call
10251			if (!func && (opline->opcode != ZEND_DO_UCALL)) {
10252				|	jmp >9
10253			}
10254			|	call ->context_threaded_call
10255			if (!func) {
10256				|	jmp >9
10257			}
10258#else
10259			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
10260				|	ADD_HYBRID_SPAD
10261				|	JMP_IP
10262			} else if (GCC_GLOBAL_REGS) {
10263				|	add r4, SPAD // stack alignment
10264				|	JMP_IP
10265			} else {
10266				|	mov FP, aword T2 // restore FP
10267				|	mov RX, aword T3 // restore IP
10268				|	add r4, NR_SPAD // stack alignment
10269				|	mov r0, 1 // ZEND_VM_ENTER
10270				|	ret
10271			}
10272		}
10273#endif
10274	}
10275
10276	if ((!func || func->type == ZEND_INTERNAL_FUNCTION)
10277	 && (opline->opcode != ZEND_DO_UCALL)) {
10278		if (!func && (opline->opcode != ZEND_DO_ICALL)) {
10279			|8:
10280		}
10281		if (opline->opcode == ZEND_DO_FCALL_BY_NAME) {
10282			if (!func) {
10283				if (trace) {
10284					uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10285
10286					exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10287					if (!exit_addr) {
10288						return 0;
10289					}
10290					|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
10291					|	jnz &exit_addr
10292				} else {
10293					|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
10294					|	jnz >1
10295						|.cold_code
10296					|1:
10297					if (!GCC_GLOBAL_REGS) {
10298						|	mov FCARG1a, RX
10299					}
10300					|	EXT_CALL zend_jit_deprecated_helper, r0
10301					|	test al, al
10302					|	mov r0, EX:RX->func // reload
10303					|	jne >1
10304					|	jmp ->exception_handler
10305					|.code
10306					|1:
10307				}
10308			} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
10309				if (!GCC_GLOBAL_REGS) {
10310					|	mov FCARG1a, RX
10311				}
10312				|	EXT_CALL zend_jit_deprecated_helper, r0
10313				|	test al, al
10314				|	je ->exception_handler
10315				|	mov r0, EX:RX->func // reload
10316			}
10317		}
10318
10319		|	// EG(current_execute_data) = execute_data;
10320		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, RX, r1
10321
10322		if (ZEND_OBSERVER_ENABLED) {
10323			|	mov FCARG1a, RX
10324			|	EXT_CALL zend_observer_fcall_begin, r0
10325			|	mov r0, EX:RX->func // reload
10326		}
10327
10328		|	// ZVAL_NULL(EX_VAR(opline->result.var));
10329		|	LOAD_ZVAL_ADDR FCARG2a, res_addr
10330		|	SET_Z_TYPE_INFO FCARG2a, IS_NULL
10331
10332		zend_jit_reset_last_valid_opline();
10333
10334		|	// (zend_execute_internal ? zend_execute_internal : fbc->internal_function.handler)(call, ret);
10335		if (zend_execute_internal) {
10336			|.if X64
10337				| // CARG2 and FCARG2a are identical
10338				|	mov CARG1, RX
10339			|.else
10340				|	mov aword A2, FCARG2a
10341				|	mov aword A1, RX
10342			|.endif
10343			|	EXT_CALL zend_execute_internal, r0
10344		} else {
10345			|	mov FCARG1a, RX
10346			if (func) {
10347				|	EXT_CALL func->internal_function.handler, r0
10348			} else {
10349				|	call aword [r0 + offsetof(zend_internal_function, handler)]
10350			}
10351		}
10352
10353		if (ZEND_OBSERVER_ENABLED) {
10354			|	LOAD_ZVAL_ADDR FCARG2a, res_addr
10355			|	mov FCARG1a, RX
10356			|	EXT_CALL zend_observer_fcall_end, r0
10357		}
10358
10359		|	// EG(current_execute_data) = execute_data;
10360		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, FP, r0
10361
10362		|	// zend_vm_stack_free_args(call);
10363		if (func && !unknown_num_args) {
10364			for (i = 0; i < call_num_args; i++ ) {
10365				if (zend_jit_needs_arg_dtor(func, i, call_info)) {
10366					uint32_t offset = EX_NUM_TO_VAR(i);
10367					|	ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_RX, offset), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 0, 1, opline
10368				}
10369			}
10370		} else {
10371			|	mov FCARG1a, RX
10372			|	EXT_CALL zend_jit_vm_stack_free_args_helper, r0
10373		}
10374		if (may_have_extra_named_params) {
10375			|	test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS >> 24)
10376			|	jnz >1
10377			|.cold_code
10378			|1:
10379			|	mov FCARG1a, aword [RX + offsetof(zend_execute_data, extra_named_params)]
10380			|	EXT_CALL zend_free_extra_named_params, r0
10381			|	jmp >2
10382			|.code
10383			|2:
10384		}
10385
10386		|8:
10387		if (opline->opcode == ZEND_DO_FCALL) {
10388			// TODO: optimize ???
10389			|	// if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS))
10390			|	test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 2], (ZEND_CALL_RELEASE_THIS >> 16)
10391			|	jnz >1
10392			|.cold_code
10393			|1:
10394			|	GET_Z_PTR FCARG1a, RX + offsetof(zend_execute_data, This)
10395			|	// OBJ_RELEASE(object);
10396			|	OBJ_RELEASE ZREG_FCARG1, >2
10397			|	jmp >2
10398			|.code
10399			|2:
10400		}
10401
10402		if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
10403		    !JIT_G(current_frame) ||
10404		    !JIT_G(current_frame)->call ||
10405		    !TRACE_FRAME_IS_NESTED(JIT_G(current_frame)->call) ||
10406		    prev_opline->opcode == ZEND_SEND_UNPACK ||
10407		    prev_opline->opcode == ZEND_SEND_ARRAY ||
10408			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
10409
10410			|	// zend_vm_stack_free_call_frame(call);
10411			|	test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 2], (ZEND_CALL_ALLOCATED >> 16)
10412			|	jnz >1
10413			|.cold_code
10414			|1:
10415			|	mov FCARG1a, RX
10416			|	EXT_CALL zend_jit_free_call_frame, r0
10417			|	jmp >1
10418			|.code
10419		}
10420		|	MEM_STORE_ZTS aword, executor_globals, vm_stack_top, RX, r0
10421		|1:
10422
10423		if (!RETURN_VALUE_USED(opline)) {
10424			zend_class_entry *ce;
10425			bool ce_is_instanceof;
10426			uint32_t func_info = call_info ?
10427				zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof) :
10428				(MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
10429
10430			/* If an exception is thrown, the return_value may stay at the
10431			 * original value of null. */
10432			func_info |= MAY_BE_NULL;
10433
10434			if (func_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
10435				|	ZVAL_PTR_DTOR res_addr, func_info, 1, 1, opline
10436			}
10437		}
10438
10439		|	// if (UNEXPECTED(EG(exception) != NULL)) {
10440		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
10441		|	jne ->icall_throw_handler
10442
10443		// TODO: Can we avoid checking for interrupts after each call ???
10444		if (trace && last_valid_opline != opline) {
10445			int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM);
10446
10447			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10448			if (!exit_addr) {
10449				return 0;
10450			}
10451		} else {
10452			exit_addr = NULL;
10453		}
10454		if (!zend_jit_check_timeout(Dst, opline + 1, exit_addr)) {
10455			return 0;
10456		}
10457
10458		if ((!trace || !func) && opline->opcode != ZEND_DO_ICALL) {
10459			|	LOAD_IP_ADDR (opline + 1)
10460		} else if (trace
10461		 && trace->op == ZEND_JIT_TRACE_END
10462		 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
10463			|	LOAD_IP_ADDR (opline + 1)
10464		}
10465	}
10466
10467	if (!func) {
10468		|9:
10469	}
10470
10471	return 1;
10472}
10473
10474static int zend_jit_send_val(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr)
10475{
10476	uint32_t arg_num = opline->op2.num;
10477	zend_jit_addr arg_addr;
10478
10479	ZEND_ASSERT(opline->opcode == ZEND_SEND_VAL || arg_num <= MAX_ARG_FLAG_NUM);
10480
10481	if (!zend_jit_reuse_ip(Dst)) {
10482		return 0;
10483	}
10484
10485	if (opline->opcode == ZEND_SEND_VAL_EX) {
10486		uint32_t mask = ZEND_SEND_BY_REF << ((arg_num + 3) * 2);
10487
10488		ZEND_ASSERT(arg_num <= MAX_ARG_FLAG_NUM);
10489
10490		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10491		 && JIT_G(current_frame)
10492		 && JIT_G(current_frame)->call
10493		 && JIT_G(current_frame)->call->func) {
10494			if (ARG_MUST_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10495				/* Don't generate code that always throws exception */
10496				return 0;
10497			}
10498		} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10499			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10500			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10501			if (!exit_addr) {
10502				return 0;
10503			}
10504			|	mov r0, EX:RX->func
10505			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10506			|	jnz &exit_addr
10507		} else {
10508			|	mov r0, EX:RX->func
10509			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10510			|	jnz >1
10511			|.cold_code
10512			|1:
10513			if (Z_MODE(op1_addr) == IS_REG) {
10514				/* set type to avoid zval_ptr_dtor() on uninitialized value */
10515				zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
10516				|	SET_ZVAL_TYPE_INFO addr, IS_UNDEF
10517			}
10518			|	SET_EX_OPLINE opline, r0
10519			|	jmp ->throw_cannot_pass_by_ref
10520			|.code
10521
10522		}
10523	}
10524
10525	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
10526
10527	if (opline->op1_type == IS_CONST) {
10528		zval *zv = RT_CONSTANT(opline, opline->op1);
10529
10530		|	ZVAL_COPY_CONST arg_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_R0
10531		if (Z_REFCOUNTED_P(zv)) {
10532			|	ADDREF_CONST zv, r0
10533		}
10534	} else {
10535		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
10536	}
10537
10538	return 1;
10539}
10540
10541static int zend_jit_check_undef_args(dasm_State **Dst, const zend_op *opline)
10542{
10543	|	mov FCARG1a, EX->call
10544	|	test byte [FCARG1a + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_MAY_HAVE_UNDEF >> 24)
10545	|	jnz >1
10546	|.cold_code
10547	|1:
10548	|	SET_EX_OPLINE opline, r0
10549	|	EXT_CALL zend_handle_undef_args, r0
10550	|	test r0, r0
10551	|	jnz ->exception_handler
10552	|	jmp >2
10553	|.code
10554	|2:
10555
10556	return 1;
10557}
10558
10559static int zend_jit_send_ref(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, int cold)
10560{
10561	zend_jit_addr op1_addr, arg_addr, ref_addr;
10562
10563	op1_addr = OP1_ADDR();
10564	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
10565
10566	if (!zend_jit_reuse_ip(Dst)) {
10567		return 0;
10568	}
10569
10570	if (opline->op1_type == IS_VAR) {
10571		if (op1_info & MAY_BE_INDIRECT) {
10572			|	LOAD_ZVAL_ADDR r0, op1_addr
10573			|	// if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) {
10574			|	IF_NOT_Z_TYPE r0, IS_INDIRECT, >1
10575			|	// ret = Z_INDIRECT_P(ret);
10576			|	GET_Z_PTR r0, r0
10577			|1:
10578			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
10579		}
10580	} else if (opline->op1_type == IS_CV) {
10581		if (op1_info & MAY_BE_UNDEF) {
10582			if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10583				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
10584				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL
10585				|	jmp >2
10586				|1:
10587			}
10588			op1_info &= ~MAY_BE_UNDEF;
10589			op1_info |= MAY_BE_NULL;
10590		}
10591	} else {
10592		ZEND_UNREACHABLE();
10593	}
10594
10595	if (op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) {
10596		if (op1_info & MAY_BE_REF) {
10597			|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >2
10598			|	GET_ZVAL_PTR r1, op1_addr
10599			|	GC_ADDREF r1
10600			|	SET_ZVAL_PTR arg_addr, r1
10601			|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX
10602			|	jmp >6
10603		}
10604		|2:
10605		|	// ZVAL_NEW_REF(arg, varptr);
10606		if (opline->op1_type == IS_VAR) {
10607			if (Z_REG(op1_addr) != ZREG_R0 || Z_OFFSET(op1_addr) != 0) {
10608				|	LOAD_ZVAL_ADDR r0, op1_addr
10609			}
10610			|	mov aword T1, r0 // save
10611		}
10612		|	EMALLOC sizeof(zend_reference), op_array, opline
10613		|	mov dword [r0], 2
10614		|	mov dword [r0 + offsetof(zend_reference, gc.u.type_info)], GC_REFERENCE
10615		|	mov aword [r0 + offsetof(zend_reference, sources.ptr)], 0
10616		ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, offsetof(zend_reference, val));
10617		if (opline->op1_type == IS_VAR) {
10618			zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R1, 0);
10619
10620			|	mov r1, aword T1 // restore
10621			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_R2, ZREG_R2
10622			|	SET_ZVAL_PTR val_addr, r0
10623			|	SET_ZVAL_TYPE_INFO val_addr, IS_REFERENCE_EX
10624		} else {
10625			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10626			|	SET_ZVAL_PTR op1_addr, r0
10627			|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX
10628		}
10629		|	SET_ZVAL_PTR arg_addr, r0
10630		|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX
10631	}
10632
10633	|6:
10634	|	FREE_OP opline->op1_type, opline->op1, op1_info, !cold, opline
10635	|7:
10636
10637	return 1;
10638}
10639
10640static 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)
10641{
10642	uint32_t arg_num = opline->op2.num;
10643	zend_jit_addr arg_addr;
10644
10645	ZEND_ASSERT((opline->opcode != ZEND_SEND_VAR_EX &&
10646	     opline->opcode != ZEND_SEND_VAR_NO_REF_EX) ||
10647	    arg_num <= MAX_ARG_FLAG_NUM);
10648
10649	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
10650
10651	if (!zend_jit_reuse_ip(Dst)) {
10652		return 0;
10653	}
10654
10655	if (opline->opcode == ZEND_SEND_VAR_EX) {
10656		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10657		 && JIT_G(current_frame)
10658		 && JIT_G(current_frame)->call
10659		 && JIT_G(current_frame)->call->func) {
10660			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10661				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
10662					return 0;
10663				}
10664				return 1;
10665			}
10666		} else {
10667			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10668
10669			|	mov r0, EX:RX->func
10670			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10671			|	jnz >1
10672			|.cold_code
10673			|1:
10674			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
10675				return 0;
10676			}
10677			|	jmp >7
10678			|.code
10679		}
10680	} else if (opline->opcode == ZEND_SEND_VAR_NO_REF_EX) {
10681		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10682		 && JIT_G(current_frame)
10683		 && JIT_G(current_frame)->call
10684		 && JIT_G(current_frame)->call->func) {
10685			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10686
10687				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10688
10689				if (!ARG_MAY_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10690					if (!(op1_info & MAY_BE_REF)) {
10691						/* Don't generate code that always throws exception */
10692						return 0;
10693					} else {
10694						int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10695						const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10696						if (!exit_addr) {
10697							return 0;
10698						}
10699						|	cmp cl, IS_REFERENCE
10700						|	jne &exit_addr
10701					}
10702				}
10703				return 1;
10704			}
10705		} else {
10706			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10707
10708			|	mov r0, EX:RX->func
10709			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10710			|	jnz >1
10711			|.cold_code
10712			|1:
10713
10714			mask = ZEND_SEND_PREFER_REF << ((arg_num + 3) * 2);
10715
10716			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10717			if (op1_info & MAY_BE_REF) {
10718				|	cmp cl, IS_REFERENCE
10719				|	je >7
10720			}
10721			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10722			|	jnz >7
10723			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10724				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10725				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10726				if (!exit_addr) {
10727					return 0;
10728				}
10729				|	jmp &exit_addr
10730			} else {
10731				|	SET_EX_OPLINE opline, r0
10732				|	LOAD_ZVAL_ADDR FCARG1a, arg_addr
10733				|	EXT_CALL zend_jit_only_vars_by_reference, r0
10734				if (!zend_jit_check_exception(Dst)) {
10735					return 0;
10736				}
10737				|	jmp >7
10738			}
10739
10740			|.code
10741		}
10742	} else if (opline->opcode == ZEND_SEND_FUNC_ARG) {
10743		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10744		 && JIT_G(current_frame)
10745		 && JIT_G(current_frame)->call
10746		 && JIT_G(current_frame)->call->func) {
10747			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10748				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
10749					return 0;
10750				}
10751				return 1;
10752			}
10753		} else {
10754			|	test dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
10755			|	jnz >1
10756			|.cold_code
10757			|1:
10758			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
10759				return 0;
10760			}
10761			|	jmp >7
10762			|.code
10763		}
10764	}
10765
10766	if (op1_info & MAY_BE_UNDEF) {
10767		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10768			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1
10769			|.cold_code
10770			|1:
10771		}
10772
10773		|	SET_EX_OPLINE opline, r0
10774		|	mov FCARG1d, opline->op1.var
10775		|	EXT_CALL zend_jit_undefined_op_helper, r0
10776		|	SET_ZVAL_TYPE_INFO arg_addr, IS_NULL
10777		|	test r0, r0
10778		|	jz ->exception_handler
10779
10780		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10781			|	jmp >7
10782			|.code
10783		} else {
10784			|7:
10785			return 1;
10786		}
10787	}
10788
10789	if (opline->opcode == ZEND_SEND_VAR_NO_REF) {
10790		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10791		if (op1_info & MAY_BE_REF) {
10792			|	cmp cl, IS_REFERENCE
10793			|	je >7
10794		}
10795		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10796			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10797			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10798			if (!exit_addr) {
10799				return 0;
10800			}
10801			|	jmp &exit_addr
10802		} else {
10803			|	SET_EX_OPLINE opline, r0
10804			|	LOAD_ZVAL_ADDR FCARG1a, arg_addr
10805			|	EXT_CALL zend_jit_only_vars_by_reference, r0
10806			if (!zend_jit_check_exception(Dst)) {
10807				return 0;
10808			}
10809		}
10810	} else {
10811		if (op1_info & MAY_BE_REF) {
10812			if (opline->op1_type == IS_CV) {
10813				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
10814
10815				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
10816				|	ZVAL_DEREF FCARG1a, op1_info
10817				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_R0, ZREG_R2
10818				|	TRY_ADDREF op1_info, ah, r2
10819			} else {
10820				zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 8);
10821
10822				|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
10823				|.cold_code
10824				|1:
10825				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
10826				|	GET_ZVAL_PTR FCARG1a, op1_addr
10827				|	// ZVAL_COPY_VALUE(return_value, &ref->value);
10828				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_R0, ZREG_R2
10829				|	GC_DELREF FCARG1a
10830				|	je >1
10831				|	IF_NOT_REFCOUNTED ah, >2
10832				|	GC_ADDREF r2
10833				|	jmp >2
10834				|1:
10835				|	EFREE_REG_REFERENCE
10836				|	jmp >2
10837				|.code
10838				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
10839				|2:
10840			}
10841		} else {
10842			if (op1_addr != op1_def_addr) {
10843				if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
10844					return 0;
10845				}
10846				if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
10847					op1_addr= op1_def_addr;
10848				}
10849			}
10850			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
10851			if (opline->op1_type == IS_CV) {
10852				|	TRY_ADDREF op1_info, ah, r2
10853			}
10854		}
10855	}
10856	|7:
10857
10858	return 1;
10859}
10860
10861static int zend_jit_check_func_arg(dasm_State **Dst, const zend_op *opline)
10862{
10863	uint32_t arg_num = opline->op2.num;
10864
10865	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10866	 && JIT_G(current_frame)
10867	 && JIT_G(current_frame)->call
10868	 && JIT_G(current_frame)->call->func) {
10869		if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10870			if (!TRACE_FRAME_IS_LAST_SEND_BY_REF(JIT_G(current_frame)->call)) {
10871				TRACE_FRAME_SET_LAST_SEND_BY_REF(JIT_G(current_frame)->call);
10872				|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10873				||	if (reuse_ip) {
10874				|		or dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
10875				||	} else {
10876				|		mov r0, EX->call
10877				|		or dword [r0 + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
10878				||	}
10879			}
10880		} else {
10881			if (!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
10882				TRACE_FRAME_SET_LAST_SEND_BY_VAL(JIT_G(current_frame)->call);
10883				|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10884				||	if (reuse_ip) {
10885				|		and dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
10886				||	} else {
10887				|		mov r0, EX->call
10888				|		and dword [r0 + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
10889				||	}
10890			}
10891		}
10892	} else {
10893		// if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
10894		uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10895
10896		if (!zend_jit_reuse_ip(Dst)) {
10897			return 0;
10898		}
10899
10900		|	mov r0, EX:RX->func
10901		|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10902		|	jnz >1
10903		|.cold_code
10904		|1:
10905		|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10906		|	or dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
10907		|	jmp >1
10908		|.code
10909		|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10910		|	and dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
10911		|1:
10912	}
10913
10914	return 1;
10915}
10916
10917static int zend_jit_smart_true(dasm_State **Dst, const zend_op *opline, int jmp, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2)
10918{
10919	if (smart_branch_opcode) {
10920		if (smart_branch_opcode == ZEND_JMPZ) {
10921			if (jmp) {
10922				|	jmp >7
10923			}
10924		} else if (smart_branch_opcode == ZEND_JMPNZ) {
10925			|	jmp =>target_label
10926		} else {
10927			ZEND_UNREACHABLE();
10928		}
10929	} else {
10930		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
10931
10932		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
10933		if (jmp) {
10934			|	jmp >7
10935		}
10936	}
10937
10938	return 1;
10939}
10940
10941static int zend_jit_smart_false(dasm_State **Dst, const zend_op *opline, int jmp, uint8_t smart_branch_opcode, uint32_t target_label)
10942{
10943	if (smart_branch_opcode) {
10944		if (smart_branch_opcode == ZEND_JMPZ) {
10945			|	jmp =>target_label
10946		} else if (smart_branch_opcode == ZEND_JMPNZ) {
10947			if (jmp) {
10948				|	jmp >7
10949			}
10950		} else {
10951			ZEND_UNREACHABLE();
10952		}
10953	} else {
10954		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
10955
10956		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
10957		if (jmp) {
10958			|	jmp >7
10959		}
10960	}
10961
10962	return 1;
10963}
10964
10965static int zend_jit_defined(dasm_State **Dst, const zend_op *opline, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
10966{
10967	uint32_t defined_label = (uint32_t)-1;
10968	uint32_t undefined_label = (uint32_t)-1;
10969	zval *zv = RT_CONSTANT(opline, opline->op1);
10970	zend_jit_addr res_addr = 0;
10971
10972	if (smart_branch_opcode && !exit_addr) {
10973		if (smart_branch_opcode == ZEND_JMPZ) {
10974			undefined_label = target_label;
10975		} else if (smart_branch_opcode == ZEND_JMPNZ) {
10976			defined_label = target_label;
10977		} else {
10978			ZEND_UNREACHABLE();
10979		}
10980	}
10981
10982	|	// if (CACHED_PTR(opline->extended_value)) {
10983	|	mov r0, EX->run_time_cache
10984	|	mov r0, aword [r0 + opline->extended_value]
10985	|	test r0, r0
10986	|	jz >1
10987	|	test r0, 0x1
10988	|	jnz >4
10989	|.cold_code
10990	|4:
10991	|	MEM_LOAD_ZTS FCARG1a, aword, executor_globals, zend_constants, FCARG1a
10992	|	shr r0, 1
10993	|	cmp dword [FCARG1a + offsetof(HashTable, nNumOfElements)], eax
10994
10995	if (smart_branch_opcode) {
10996		if (exit_addr) {
10997			if (smart_branch_opcode == ZEND_JMPZ) {
10998				|	jz &exit_addr
10999			} else {
11000				|	jz >3
11001			}
11002		} else if (undefined_label != (uint32_t)-1) {
11003			|	jz =>undefined_label
11004		} else {
11005			|	jz >3
11006		}
11007	} else {
11008		|	jz >2
11009	}
11010	|1:
11011	|	SET_EX_OPLINE opline, r0
11012	|	LOAD_ADDR FCARG1a, zv
11013	|	EXT_CALL zend_jit_check_constant, r0
11014	|	test r0, r0
11015	if (exit_addr) {
11016		if (smart_branch_opcode == ZEND_JMPNZ) {
11017			|	jz >3
11018		} else {
11019			|	jnz >3
11020		}
11021		|	jmp &exit_addr
11022	} else if (smart_branch_opcode) {
11023		if (undefined_label != (uint32_t)-1) {
11024			|	jz =>undefined_label
11025		} else {
11026			|	jz >3
11027		}
11028		if (defined_label != (uint32_t)-1) {
11029			|	jmp =>defined_label
11030		} else {
11031			|	jmp >3
11032		}
11033	} else {
11034		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11035		|	jnz >1
11036		|2:
11037		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
11038		|	jmp >3
11039	}
11040	|.code
11041	if (smart_branch_opcode) {
11042		if (exit_addr) {
11043			if (smart_branch_opcode == ZEND_JMPNZ) {
11044				|	jmp &exit_addr
11045			}
11046		} else if (defined_label != (uint32_t)-1) {
11047			|	jmp =>defined_label
11048		}
11049	} else {
11050		|1:
11051		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
11052	}
11053	|3:
11054
11055	return 1;
11056}
11057
11058static int zend_jit_type_check(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
11059{
11060	uint32_t  mask;
11061	zend_jit_addr op1_addr = OP1_ADDR();
11062
11063	// TODO: support for is_resource() ???
11064	ZEND_ASSERT(opline->extended_value != MAY_BE_RESOURCE);
11065
11066	if (op1_info & MAY_BE_UNDEF) {
11067		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
11068			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1
11069			|.cold_code
11070			|1:
11071		}
11072		|	SET_EX_OPLINE opline, r0
11073		|	mov FCARG1d, opline->op1.var
11074		|	EXT_CALL zend_jit_undefined_op_helper, r0
11075		zend_jit_check_exception_undef_result(Dst, opline);
11076		if (opline->extended_value & MAY_BE_NULL) {
11077			if (exit_addr) {
11078				if (smart_branch_opcode == ZEND_JMPNZ) {
11079					|	jmp &exit_addr
11080				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
11081					|	jmp >7
11082				}
11083			} else if (!zend_jit_smart_true(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label, target_label2)) {
11084				return 0;
11085			}
11086		} else {
11087			if (exit_addr) {
11088				if (smart_branch_opcode == ZEND_JMPZ) {
11089					|	jmp &exit_addr
11090				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
11091					|	jmp >7
11092				}
11093			} else if (!zend_jit_smart_false(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label)) {
11094				return 0;
11095			}
11096		}
11097		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
11098			|.code
11099		}
11100	}
11101
11102	if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
11103		mask = opline->extended_value;
11104		if (!(op1_info & MAY_BE_GUARD) && !(op1_info & (MAY_BE_ANY - mask))) {
11105			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11106			if (exit_addr) {
11107				if (smart_branch_opcode == ZEND_JMPNZ) {
11108					|	jmp &exit_addr
11109				}
11110			} else if (!zend_jit_smart_true(Dst, opline, 0, smart_branch_opcode, target_label, target_label2)) {
11111				return 0;
11112			}
11113	    } else if (!(op1_info & MAY_BE_GUARD) && !(op1_info & mask)) {
11114			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11115			if (exit_addr) {
11116				if (smart_branch_opcode == ZEND_JMPZ) {
11117					|	jmp &exit_addr
11118				}
11119			} else if (!zend_jit_smart_false(Dst, opline, 0, smart_branch_opcode, target_label)) {
11120				return 0;
11121			}
11122		} else {
11123			bool invert = 0;
11124			uint8_t type;
11125
11126			switch (mask) {
11127				case MAY_BE_NULL:   type = IS_NULL;   break;
11128				case MAY_BE_FALSE:  type = IS_FALSE;  break;
11129				case MAY_BE_TRUE:   type = IS_TRUE;   break;
11130				case MAY_BE_LONG:   type = IS_LONG;   break;
11131				case MAY_BE_DOUBLE: type = IS_DOUBLE; break;
11132				case MAY_BE_STRING: type = IS_STRING; break;
11133				case MAY_BE_ARRAY:  type = IS_ARRAY;  break;
11134				case MAY_BE_OBJECT: type = IS_OBJECT; break;
11135				case MAY_BE_ANY - MAY_BE_NULL:     type = IS_NULL;   invert = 1; break;
11136				case MAY_BE_ANY - MAY_BE_FALSE:    type = IS_FALSE;  invert = 1; break;
11137				case MAY_BE_ANY - MAY_BE_TRUE:     type = IS_TRUE;   invert = 1; break;
11138				case MAY_BE_ANY - MAY_BE_LONG:     type = IS_LONG;   invert = 1; break;
11139				case MAY_BE_ANY - MAY_BE_DOUBLE:   type = IS_DOUBLE; invert = 1; break;
11140				case MAY_BE_ANY - MAY_BE_STRING:   type = IS_STRING; invert = 1; break;
11141				case MAY_BE_ANY - MAY_BE_ARRAY:    type = IS_ARRAY;  invert = 1; break;
11142				case MAY_BE_ANY - MAY_BE_OBJECT:   type = IS_OBJECT; invert = 1; break;
11143				case MAY_BE_ANY - MAY_BE_RESOURCE: type = IS_OBJECT; invert = 1; break;
11144				default:
11145					type = 0;
11146			}
11147
11148			if (op1_info & MAY_BE_REF) {
11149				|	LOAD_ZVAL_ADDR r0, op1_addr
11150				|	ZVAL_DEREF r0, op1_info
11151			}
11152			if (type == 0) {
11153				if (smart_branch_opcode &&
11154				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
11155				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11156					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11157						|	// if (Z_REFCOUNTED_P(cv)) {
11158						|	IF_ZVAL_REFCOUNTED op1_addr, >1
11159						|.cold_code
11160						|1:
11161					}
11162					|	// if (!Z_DELREF_P(cv)) {
11163					|	GET_ZVAL_PTR FCARG1a, op1_addr
11164					|	GC_DELREF FCARG1a
11165					if (RC_MAY_BE_1(op1_info)) {
11166						if (RC_MAY_BE_N(op1_info)) {
11167							|	jnz >3
11168						}
11169						if (op1_info & MAY_BE_REF) {
11170							|	mov al, byte [r0 + 8]
11171						} else {
11172							|	mov al, byte [FP + opline->op1.var + 8]
11173						}
11174						|	mov byte T1, al // save
11175						|	// zval_dtor_func(r);
11176						|	ZVAL_DTOR_FUNC op1_info, opline
11177						|	mov cl, byte T1 // restore
11178						|jmp >2
11179					}
11180					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11181						if (!RC_MAY_BE_1(op1_info)) {
11182							|	jmp >3
11183						}
11184						|.code
11185					}
11186					|3:
11187					if (op1_info & MAY_BE_REF) {
11188						|	mov cl, byte [r0 + 8]
11189					} else {
11190						|	mov cl, byte [FP + opline->op1.var + 8]
11191					}
11192					|2:
11193				} else {
11194					if (op1_info & MAY_BE_REF) {
11195						|	mov cl, byte [r0 + 8]
11196					} else {
11197						|	mov cl, byte [FP + opline->op1.var + 8]
11198					}
11199				}
11200				|	mov eax, 1
11201				|	shl eax, cl
11202				|	test eax, mask
11203				if (exit_addr) {
11204					if (smart_branch_opcode == ZEND_JMPNZ) {
11205						|	jne &exit_addr
11206					} else {
11207						|	je &exit_addr
11208					}
11209				} else if (smart_branch_opcode) {
11210					if (smart_branch_opcode == ZEND_JMPZ) {
11211						|	je =>target_label
11212					} else if (smart_branch_opcode == ZEND_JMPNZ) {
11213						|	jne =>target_label
11214					} else {
11215						ZEND_UNREACHABLE();
11216					}
11217				} else {
11218					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11219
11220					|	setne al
11221					|	movzx eax, al
11222					|	add eax, 2
11223					|	SET_ZVAL_TYPE_INFO res_addr, eax
11224					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11225				}
11226			} else {
11227				if (smart_branch_opcode &&
11228				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
11229				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11230					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11231						|	// if (Z_REFCOUNTED_P(cv)) {
11232						|	IF_ZVAL_REFCOUNTED op1_addr, >1
11233						|.cold_code
11234						|1:
11235					}
11236					|	// if (!Z_DELREF_P(cv)) {
11237					|	GET_ZVAL_PTR FCARG1a, op1_addr
11238					|	GC_DELREF FCARG1a
11239					if (RC_MAY_BE_1(op1_info)) {
11240						if (RC_MAY_BE_N(op1_info)) {
11241							|	jnz >3
11242						}
11243						if (op1_info & MAY_BE_REF) {
11244							|	mov al, byte [r0 + 8]
11245						} else {
11246							|	mov al, byte [FP + opline->op1.var + 8]
11247						}
11248						|	mov byte T1, al // save
11249						|	// zval_dtor_func(r);
11250						|	ZVAL_DTOR_FUNC op1_info, opline
11251						|	mov cl, byte T1 // restore
11252						|jmp >2
11253					}
11254					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11255						if (!RC_MAY_BE_1(op1_info)) {
11256							|	jmp >3
11257						}
11258						|.code
11259					}
11260					|3:
11261					if (op1_info & MAY_BE_REF) {
11262						|	mov cl, byte [r0 + 8]
11263					} else {
11264						|	mov cl, byte [FP + opline->op1.var + 8]
11265					}
11266					|2:
11267					|	cmp cl, type
11268				} else {
11269					if (op1_info & MAY_BE_REF) {
11270						|	cmp byte [r0 + 8], type
11271					} else {
11272						|	cmp byte [FP + opline->op1.var + 8], type
11273					}
11274				}
11275				if (exit_addr) {
11276					if (invert) {
11277						if (smart_branch_opcode == ZEND_JMPNZ) {
11278							|	jne &exit_addr
11279						} else {
11280							|	je &exit_addr
11281						}
11282					} else {
11283						if (smart_branch_opcode == ZEND_JMPNZ) {
11284							|	je &exit_addr
11285						} else {
11286							|	jne &exit_addr
11287						}
11288					}
11289				} else if (smart_branch_opcode) {
11290					if (invert) {
11291						if (smart_branch_opcode == ZEND_JMPZ) {
11292							|	je =>target_label
11293						} else if (smart_branch_opcode == ZEND_JMPNZ) {
11294							|	jne =>target_label
11295						} else {
11296							ZEND_UNREACHABLE();
11297						}
11298					} else {
11299						if (smart_branch_opcode == ZEND_JMPZ) {
11300							|	jne =>target_label
11301						} else if (smart_branch_opcode == ZEND_JMPNZ) {
11302							|	je =>target_label
11303						} else {
11304							ZEND_UNREACHABLE();
11305						}
11306					}
11307				} else {
11308					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11309
11310					if (invert) {
11311						|	setne al
11312					} else {
11313						|	sete al
11314					}
11315					|	movzx eax, al
11316					|	add eax, 2
11317					|	SET_ZVAL_TYPE_INFO res_addr, eax
11318					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11319				}
11320			}
11321	    }
11322	}
11323
11324	|7:
11325
11326	return 1;
11327}
11328
11329static int zend_jit_leave_frame(dasm_State **Dst)
11330{
11331	|	// EG(current_execute_data) = EX(prev_execute_data);
11332	|	mov r0, EX->prev_execute_data
11333	|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, r0, r2
11334	return 1;
11335}
11336
11337static int zend_jit_free_cvs(dasm_State **Dst)
11338{
11339	|	// EG(current_execute_data) = EX(prev_execute_data);
11340	|	mov FCARG1a, EX->prev_execute_data
11341	|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, FCARG1a, r0
11342	|	// zend_free_compiled_variables(execute_data);
11343	|	mov FCARG1a, FP
11344	|	EXT_CALL zend_free_compiled_variables, r0
11345	return 1;
11346}
11347
11348static int zend_jit_free_cv(dasm_State **Dst, uint32_t info, uint32_t var)
11349{
11350	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
11351		uint32_t offset = EX_NUM_TO_VAR(var);
11352		| ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_FP, offset), info, 1, 1, NULL
11353	}
11354	return 1;
11355}
11356
11357static int zend_jit_free_op(dasm_State **Dst, const zend_op *opline, uint32_t info, uint32_t var_offset)
11358{
11359	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
11360		| ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_FP, var_offset), info, 0, 1, opline
11361	}
11362	return 1;
11363}
11364
11365static int zend_jit_leave_func(dasm_State          **Dst,
11366                               const zend_op_array  *op_array,
11367                               const zend_op        *opline,
11368                               uint32_t              op1_info,
11369                               bool             left_frame,
11370                               zend_jit_trace_rec   *trace,
11371                               zend_jit_trace_info  *trace_info,
11372                               int                   indirect_var_access,
11373                               int                   may_throw)
11374{
11375	bool may_be_top_frame =
11376		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11377		!JIT_G(current_frame) ||
11378		!TRACE_FRAME_IS_NESTED(JIT_G(current_frame));
11379	bool may_need_call_helper =
11380		indirect_var_access || /* may have symbol table */
11381		!op_array->function_name || /* may have symbol table */
11382		may_be_top_frame ||
11383		(op_array->fn_flags & ZEND_ACC_VARIADIC) || /* may have extra named args */
11384		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11385		!JIT_G(current_frame) ||
11386		TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) == -1 || /* unknown number of args */
11387		(uint32_t)TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) > op_array->num_args; /* extra args */
11388	bool may_need_release_this =
11389		!(op_array->fn_flags & ZEND_ACC_CLOSURE) &&
11390		op_array->scope &&
11391		!(op_array->fn_flags & ZEND_ACC_STATIC) &&
11392		(JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11393		 !JIT_G(current_frame) ||
11394		 !TRACE_FRAME_NO_NEED_RELEASE_THIS(JIT_G(current_frame)));
11395
11396	if (may_need_release_this) {
11397		|	mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)]
11398	}
11399	if (may_need_call_helper) {
11400		if (!left_frame) {
11401			left_frame = 1;
11402		    if (!zend_jit_leave_frame(Dst)) {
11403				return 0;
11404		    }
11405		}
11406		/* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */
11407		if (may_need_release_this) {
11408			|	test FCARG1d, (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)
11409		} else {
11410			|	test dword [FP + offsetof(zend_execute_data, This.u1.type_info)], (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)
11411		}
11412		if (trace && trace->op != ZEND_JIT_TRACE_END) {
11413			|	jnz >1
11414			|.cold_code
11415			|1:
11416			if (!GCC_GLOBAL_REGS) {
11417				|	mov FCARG1a, FP
11418			}
11419			|	EXT_CALL zend_jit_leave_func_helper, r0
11420
11421			if (may_be_top_frame) {
11422				// TODO: try to avoid this check ???
11423				if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
11424#if 0
11425					/* this check should be handled by the following OPLINE guard */
11426					|	cmp IP, zend_jit_halt_op
11427					|	je ->trace_halt
11428#endif
11429				} else if (GCC_GLOBAL_REGS) {
11430					|	test IP, IP
11431					|	je ->trace_halt
11432				} else {
11433					|	test eax, eax
11434					|	jl ->trace_halt
11435				}
11436			}
11437
11438			if (!GCC_GLOBAL_REGS) {
11439				|	// execute_data = EG(current_execute_data)
11440				|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
11441			}
11442			|	jmp >8
11443			|.code
11444		} else {
11445			|	jnz ->leave_function_handler
11446		}
11447	}
11448
11449	if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
11450		if (!left_frame) {
11451			left_frame = 1;
11452		    if (!zend_jit_leave_frame(Dst)) {
11453				return 0;
11454		    }
11455		}
11456		|	// OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
11457		|	mov FCARG1a, EX->func
11458		|	sub FCARG1a, sizeof(zend_object)
11459		|	OBJ_RELEASE ZREG_FCARG1, >4
11460		|4:
11461	} else if (may_need_release_this) {
11462		if (!left_frame) {
11463			left_frame = 1;
11464		    if (!zend_jit_leave_frame(Dst)) {
11465				return 0;
11466		    }
11467		}
11468		if (!JIT_G(current_frame) || !TRACE_FRAME_ALWAYS_RELEASE_THIS(JIT_G(current_frame))) {
11469			|	// if (call_info & ZEND_CALL_RELEASE_THIS)
11470			|	test FCARG1d, ZEND_CALL_RELEASE_THIS
11471			|	je >4
11472		}
11473		|	// zend_object *object = Z_OBJ(execute_data->This);
11474		|	mov FCARG1a, EX->This.value.obj
11475		|	// OBJ_RELEASE(object);
11476		|	OBJ_RELEASE ZREG_FCARG1, >4
11477		|4:
11478		// TODO: avoid EG(excption) check for $this->foo() calls
11479		may_throw = 1;
11480	}
11481
11482	|	// EG(vm_stack_top) = (zval*)execute_data;
11483	|	MEM_STORE_ZTS aword, executor_globals, vm_stack_top, FP, r0
11484	|	// execute_data = EX(prev_execute_data);
11485	|	mov FP, EX->prev_execute_data
11486
11487	if (!left_frame) {
11488		|	// EG(current_execute_data) = execute_data;
11489		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, FP, r0
11490	}
11491
11492	|9:
11493	if (trace) {
11494		if (trace->op != ZEND_JIT_TRACE_END
11495		 && (JIT_G(current_frame) && !TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
11496			zend_jit_reset_last_valid_opline();
11497		} else {
11498			|	LOAD_IP
11499			|	ADD_IP sizeof(zend_op)
11500		}
11501
11502		|8:
11503
11504		if (trace->op == ZEND_JIT_TRACE_BACK
11505		 && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
11506			const zend_op *next_opline = trace->opline;
11507
11508			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
11509			 && (op1_info & MAY_BE_RC1)
11510			 && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
11511				/* exception might be thrown during destruction of unused return value */
11512				|	// if (EG(exception))
11513				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
11514				|	jne ->leave_throw_handler
11515			}
11516			do {
11517				trace++;
11518			} while (trace->op == ZEND_JIT_TRACE_INIT_CALL);
11519			ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
11520			next_opline = trace->opline;
11521			ZEND_ASSERT(next_opline != NULL);
11522
11523			if (trace->op == ZEND_JIT_TRACE_END
11524			 && trace->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
11525				trace_info->flags |= ZEND_JIT_TRACE_LOOP;
11526				|	CMP_IP next_opline
11527				|	je =>0 // LOOP
11528#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
11529				|	JMP_IP
11530#else
11531				|	jmp ->trace_escape
11532#endif
11533			} else {
11534				|	CMP_IP next_opline
11535				|	jne ->trace_escape
11536			}
11537
11538			zend_jit_set_last_valid_opline(trace->opline);
11539
11540			return 1;
11541		} else if (may_throw ||
11542				(((opline->op1_type & (IS_VAR|IS_TMP_VAR))
11543				  && (op1_info & MAY_BE_RC1)
11544				  && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)))
11545				 && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) {
11546			|	// if (EG(exception))
11547			|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
11548			|	jne ->leave_throw_handler
11549		}
11550
11551		return 1;
11552	} else {
11553		|	// if (EG(exception))
11554		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
11555		|	LOAD_IP
11556		|	jne ->leave_throw_handler
11557		|	// opline = EX(opline) + 1
11558		|	ADD_IP sizeof(zend_op)
11559	}
11560
11561	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
11562		|	ADD_HYBRID_SPAD
11563#ifdef CONTEXT_THREADED_JIT
11564		|	push aword [IP]
11565		|	ret
11566#else
11567		|	JMP_IP
11568#endif
11569	} else if (GCC_GLOBAL_REGS) {
11570		|	add r4, SPAD // stack alignment
11571#ifdef CONTEXT_THREADED_JIT
11572		|	push aword [IP]
11573		|	ret
11574#else
11575		|	JMP_IP
11576#endif
11577	} else {
11578#ifdef CONTEXT_THREADED_JIT
11579		ZEND_UNREACHABLE();
11580		// TODO: context threading can't work without GLOBAL REGS because we have to change
11581		//       the value of execute_data in execute_ex()
11582		|	mov FCARG1a, FP
11583		|	mov r0, aword [FP]
11584		|	mov FP, aword T2 // restore FP
11585		|	mov RX, aword T3 // restore IP
11586		|	add r4, NR_SPAD // stack alignment
11587		|	push aword [r0]
11588		|	ret
11589#else
11590		|	mov FP, aword T2 // restore FP
11591		|	mov RX, aword T3 // restore IP
11592		|	add r4, NR_SPAD // stack alignment
11593		|	mov r0, 2 // ZEND_VM_LEAVE
11594		|	ret
11595#endif
11596	}
11597
11598	return 1;
11599}
11600
11601static 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)
11602{
11603	zend_jit_addr ret_addr;
11604	int8_t return_value_used;
11605
11606	ZEND_ASSERT(op_array->type != ZEND_EVAL_CODE && op_array->function_name);
11607	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF));
11608
11609	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && JIT_G(current_frame)) {
11610		if (TRACE_FRAME_IS_RETURN_VALUE_USED(JIT_G(current_frame))) {
11611			return_value_used = 1;
11612		} else if (TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))) {
11613			return_value_used = 0;
11614		} else {
11615			return_value_used = -1;
11616		}
11617	} else {
11618		return_value_used = -1;
11619	}
11620
11621	if (ZEND_OBSERVER_ENABLED) {
11622		if (Z_MODE(op1_addr) == IS_REG) {
11623			zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
11624
11625			if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) {
11626				return 0;
11627			}
11628			op1_addr = dst;
11629		}
11630		|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
11631		|	mov FCARG1a, FP
11632		|	SET_EX_OPLINE opline, r0
11633		|	EXT_CALL zend_observer_fcall_end, r0
11634	}
11635
11636	// if (!EX(return_value))
11637	if (Z_MODE(op1_addr) == IS_REG && Z_REG(op1_addr) == ZREG_R1) {
11638		if (return_value_used != 0) {
11639			|	mov r2, EX->return_value
11640		}
11641		if (return_value_used == -1) {
11642			|	test r2, r2
11643		}
11644		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R2, 0);
11645	} else {
11646		if (return_value_used != 0) {
11647			|	mov r1, EX->return_value
11648		}
11649		if (return_value_used == -1) {
11650			|	test r1, r1
11651		}
11652		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R1, 0);
11653	}
11654	if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
11655	    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11656		if (return_value_used == -1) {
11657			|	jz >1
11658			|.cold_code
11659			|1:
11660		}
11661		if (return_value_used != 1) {
11662			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11663				if (jit_return_label >= 0) {
11664					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, =>jit_return_label
11665				} else {
11666					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >9
11667				}
11668			}
11669			|	GET_ZVAL_PTR FCARG1a, op1_addr
11670			|	GC_DELREF FCARG1a
11671			if (RC_MAY_BE_1(op1_info)) {
11672				if (RC_MAY_BE_N(op1_info)) {
11673					if (jit_return_label >= 0) {
11674						|	jnz =>jit_return_label
11675					} else {
11676						|	jnz >9
11677					}
11678				}
11679				|	//SAVE_OPLINE()
11680				|	ZVAL_DTOR_FUNC op1_info, opline
11681				|	//????mov r1, EX->return_value // reload ???
11682			}
11683			if (return_value_used == -1) {
11684				if (jit_return_label >= 0) {
11685					|	jmp =>jit_return_label
11686				} else {
11687					|	jmp >9
11688				}
11689				|.code
11690			}
11691		}
11692	} else if (return_value_used == -1) {
11693		if (jit_return_label >= 0) {
11694			|	jz =>jit_return_label
11695		} else {
11696			|	jz >9
11697		}
11698	}
11699
11700	if (return_value_used == 0) {
11701		|9:
11702		return 1;
11703	}
11704
11705	if (opline->op1_type == IS_CONST) {
11706		zval *zv = RT_CONSTANT(opline, opline->op1);
11707		|	ZVAL_COPY_CONST ret_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_R0
11708		if (Z_REFCOUNTED_P(zv)) {
11709			|	ADDREF_CONST zv, r0
11710		}
11711	} else if (opline->op1_type == IS_TMP_VAR) {
11712		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
11713	} else if (opline->op1_type == IS_CV) {
11714		if (op1_info & MAY_BE_REF) {
11715			|	LOAD_ZVAL_ADDR r0, op1_addr
11716			|	ZVAL_DEREF r0, op1_info
11717			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
11718		}
11719		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
11720		if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
11721			if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11722			    (op1_info & (MAY_BE_REF|MAY_BE_OBJECT)) ||
11723			    !op_array->function_name) {
11724				|	TRY_ADDREF op1_info, ah, r2
11725			} else if (return_value_used != 1) {
11726				|	// if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) ZVAL_NULL(retval_ptr);
11727				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL
11728			}
11729		}
11730	} else {
11731		if (op1_info & MAY_BE_REF) {
11732			zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, offsetof(zend_reference, val));
11733
11734			|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
11735			|.cold_code
11736			|1:
11737			|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
11738			|	GET_ZVAL_PTR r0, op1_addr
11739			|	// ZVAL_COPY_VALUE(return_value, &ref->value);
11740			|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_R2, ZREG_R2
11741			|	GC_DELREF r0
11742			|	je >2
11743			|	// if (IS_REFCOUNTED())
11744			if (jit_return_label >= 0) {
11745				|	IF_NOT_REFCOUNTED dh, =>jit_return_label
11746			} else {
11747				|	IF_NOT_REFCOUNTED dh, >9
11748			}
11749			|	// ADDREF
11750			|	GET_ZVAL_PTR r2, ret_addr // reload
11751			|	GC_ADDREF r2
11752			if (jit_return_label >= 0) {
11753				|	jmp =>jit_return_label
11754			} else {
11755				|	jmp >9
11756			}
11757			|2:
11758			|	EFREE_REFERENCE r0
11759			if (jit_return_label >= 0) {
11760				|	jmp =>jit_return_label
11761			} else {
11762				|	jmp >9
11763			}
11764			|.code
11765		}
11766		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
11767	}
11768
11769	|9:
11770	return 1;
11771}
11772
11773static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, zend_jit_addr val_addr, zend_reg type_reg)
11774{
11775	ZEND_ASSERT(type_reg == ZREG_R2);
11776
11777	|.if not(X64)
11778	||	if (Z_REG(val_addr) == ZREG_R1) {
11779	|	GET_ZVAL_W2 r0, val_addr
11780	||	}
11781	|.endif
11782	|	GET_ZVAL_PTR r1, val_addr
11783	|.if not(X64)
11784	||	if (Z_REG(val_addr) != ZREG_R1) {
11785	|	GET_ZVAL_W2 r0, val_addr
11786	||	}
11787	|.endif
11788	|	IF_NOT_REFCOUNTED dh, >2
11789	|	IF_NOT_TYPE dl, IS_REFERENCE, >1
11790	|	GET_Z_TYPE_INFO edx, r1+offsetof(zend_reference, val)
11791	|.if not(X64)
11792	|	GET_Z_W2 r0, r1+offsetof(zend_reference, val)
11793	|.endif
11794	|	GET_Z_PTR r1, r1+offsetof(zend_reference, val)
11795	|	IF_NOT_REFCOUNTED dh, >2
11796	|1:
11797	|	GC_ADDREF r1
11798	|2:
11799	|	SET_ZVAL_PTR res_addr, r1
11800	|.if not(X64)
11801	|	SET_ZVAL_W2 res_addr, r0
11802	|.endif
11803	|	SET_ZVAL_TYPE_INFO res_addr, edx
11804
11805	return 1;
11806}
11807
11808static int zend_jit_fetch_dim_read(dasm_State        **Dst,
11809                                   const zend_op      *opline,
11810                                   zend_ssa           *ssa,
11811                                   const zend_ssa_op  *ssa_op,
11812                                   uint32_t            op1_info,
11813                                   zend_jit_addr       op1_addr,
11814                                   bool           op1_avoid_refcounting,
11815                                   uint32_t            op2_info,
11816                                   uint32_t            res_info,
11817                                   zend_jit_addr       res_addr,
11818                                   uint8_t             dim_type)
11819{
11820	zend_jit_addr orig_op1_addr, op2_addr;
11821	const void *exit_addr = NULL;
11822	const void *not_found_exit_addr = NULL;
11823	const void *res_exit_addr = NULL;
11824	bool result_avoid_refcounting = 0;
11825	uint32_t may_be_string = (opline->opcode != ZEND_FETCH_LIST_R) ? MAY_BE_STRING : 0;
11826	int may_throw = 0;
11827
11828	orig_op1_addr = OP1_ADDR();
11829	op2_addr = OP2_ADDR();
11830
11831	if (opline->opcode != ZEND_FETCH_DIM_IS
11832	 && JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
11833		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
11834		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
11835		if (!exit_addr) {
11836			return 0;
11837		}
11838	}
11839
11840	if ((res_info & MAY_BE_GUARD)
11841	 && JIT_G(current_frame)
11842	 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
11843		uint32_t flags = 0;
11844		uint32_t old_op1_info = 0;
11845		uint32_t old_info;
11846		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
11847		int32_t exit_point;
11848
11849		if (opline->opcode != ZEND_FETCH_LIST_R
11850		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR))
11851		 && !op1_avoid_refcounting) {
11852			flags |= ZEND_JIT_EXIT_FREE_OP1;
11853		}
11854		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
11855		 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11856			flags |= ZEND_JIT_EXIT_FREE_OP2;
11857		}
11858		if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
11859		 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
11860		 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
11861		 && (ssa_op+1)->op1_use == ssa_op->result_def
11862		 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))
11863		 && zend_jit_may_avoid_refcounting(opline+1, res_info)) {
11864			result_avoid_refcounting = 1;
11865			ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
11866		}
11867
11868		if (op1_avoid_refcounting) {
11869			old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
11870			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
11871		}
11872
11873		if (!(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))) {
11874			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
11875			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
11876			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
11877			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
11878			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
11879			res_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
11880			if (!res_exit_addr) {
11881				return 0;
11882			}
11883			res_info &= ~MAY_BE_GUARD;
11884			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
11885		}
11886
11887		if (opline->opcode == ZEND_FETCH_DIM_IS
11888		 && !(res_info & MAY_BE_NULL)) {
11889			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
11890			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_NULL, 0);
11891			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_NULL);
11892			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
11893			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
11894			not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
11895			if (!not_found_exit_addr) {
11896				return 0;
11897			}
11898		}
11899
11900		if (op1_avoid_refcounting) {
11901			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
11902		}
11903	}
11904
11905	if (op1_info & MAY_BE_REF) {
11906		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
11907		|	ZVAL_DEREF FCARG1a, op1_info
11908		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
11909	}
11910
11911	if (op1_info & MAY_BE_ARRAY) {
11912		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
11913			if (exit_addr && !(op1_info & (MAY_BE_OBJECT|may_be_string))) {
11914				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, &exit_addr
11915			} else {
11916				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
11917			}
11918		}
11919		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
11920		if ((op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) ||
11921		    (opline->opcode != ZEND_FETCH_DIM_IS && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE)) {
11922			may_throw = 1;
11923		}
11924		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, dim_type, res_exit_addr, not_found_exit_addr, exit_addr)) {
11925			return 0;
11926		}
11927	}
11928
11929	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
11930		if (op1_info & MAY_BE_ARRAY) {
11931			|.cold_code
11932			|7:
11933		}
11934
11935		if (opline->opcode != ZEND_FETCH_LIST_R && (op1_info & MAY_BE_STRING)) {
11936			may_throw = 1;
11937			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING))) {
11938				if (exit_addr && !(op1_info & MAY_BE_OBJECT)) {
11939					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &exit_addr
11940				} else {
11941					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6
11942				}
11943			}
11944			|	SET_EX_OPLINE opline, r0
11945			|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
11946			if (opline->opcode != ZEND_FETCH_DIM_IS) {
11947				if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) {
11948					|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
11949					|	EXT_CALL zend_jit_fetch_dim_str_offset_r_helper, r0
11950				} else {
11951					|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
11952					|	EXT_CALL zend_jit_fetch_dim_str_r_helper, r0
11953				}
11954				|	SET_ZVAL_PTR res_addr, r0
11955				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING
11956			} else {
11957				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
11958				|.if X64
11959					|   LOAD_ZVAL_ADDR CARG3, res_addr
11960				|.else
11961					|	sub r4, 12
11962					|   PUSH_ZVAL_ADDR res_addr, r0
11963				|.endif
11964				|	EXT_CALL zend_jit_fetch_dim_str_is_helper, r0
11965				|.if not(X64)
11966				|	add r4, 12
11967				|.endif
11968			}
11969			if ((op1_info & MAY_BE_ARRAY) ||
11970				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING)))) {
11971				|	jmp >9 // END
11972			}
11973			|6:
11974		}
11975
11976		if (op1_info & MAY_BE_OBJECT) {
11977			may_throw = 1;
11978			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string))) {
11979				if (exit_addr) {
11980					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
11981				} else {
11982					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >6
11983				}
11984			}
11985			|	SET_EX_OPLINE opline, r0
11986		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
11987				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
11988		    }
11989			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
11990				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
11991				|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
11992			} else {
11993				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
11994			}
11995			|.if X64
11996				|   LOAD_ZVAL_ADDR CARG3, res_addr
11997			|.else
11998				|	sub r4, 12
11999				|   PUSH_ZVAL_ADDR res_addr, r0
12000			|.endif
12001			if (opline->opcode != ZEND_FETCH_DIM_IS) {
12002				|	EXT_CALL zend_jit_fetch_dim_obj_r_helper, r0
12003			} else {
12004				|	EXT_CALL zend_jit_fetch_dim_obj_is_helper, r0
12005			}
12006			|.if not(X64)
12007			|	add r4, 12
12008			|.endif
12009			if ((op1_info & MAY_BE_ARRAY) ||
12010				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) {
12011				|	jmp >9 // END
12012			}
12013			|6:
12014		}
12015
12016		if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))
12017		 && (!exit_addr || !(op1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) {
12018			if ((opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) || (op2_info & MAY_BE_UNDEF)) {
12019				|	SET_EX_OPLINE opline, r0
12020				if (opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) {
12021					may_throw = 1;
12022					|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
12023					|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
12024					|	mov FCARG1d, opline->op1.var
12025					|	EXT_CALL zend_jit_undefined_op_helper, r0
12026					|1:
12027				}
12028
12029				if (op2_info & MAY_BE_UNDEF) {
12030					may_throw = 1;
12031					|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1
12032					|	mov FCARG1d, opline->op2.var
12033					|	EXT_CALL zend_jit_undefined_op_helper, r0
12034					|1:
12035				}
12036			}
12037
12038			if (opline->opcode != ZEND_FETCH_DIM_IS && opline->opcode != ZEND_FETCH_LIST_R) {
12039				may_throw = 1;
12040				if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
12041					|	LOAD_ZVAL_ADDR FCARG1a, orig_op1_addr
12042				} else {
12043					|	SET_EX_OPLINE opline, r0
12044					if (Z_MODE(op1_addr) != IS_MEM_ZVAL ||
12045					    Z_REG(op1_addr) != ZREG_FCARG1 ||
12046					    Z_OFFSET(op1_addr) != 0) {
12047						|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12048					}
12049				}
12050				|	EXT_CALL zend_jit_invalid_array_access, r0
12051			}
12052			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
12053			if (op1_info & MAY_BE_ARRAY) {
12054				|	jmp >9 // END
12055			}
12056		}
12057
12058		if (op1_info & MAY_BE_ARRAY) {
12059			|.code
12060		}
12061	}
12062
12063	if (op1_info & MAY_BE_ARRAY) {
12064		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
12065
12066		|8:
12067		if (res_exit_addr) {
12068			uint8_t type = concrete_type(res_info);
12069
12070			if ((op1_info & MAY_BE_ARRAY_OF_REF)
12071			 && dim_type != IS_UNKNOWN
12072			 && dim_type != IS_REFERENCE) {
12073				if (type < IS_STRING) {
12074					|	IF_NOT_ZVAL_TYPE val_addr, type, >1
12075					|.cold_code
12076					|1:
12077					|	IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, &res_exit_addr
12078					|	GET_Z_PTR r0, r0
12079					|	add r0, offsetof(zend_reference, val)
12080					|	IF_ZVAL_TYPE val_addr, type, >1
12081					|	jmp &res_exit_addr
12082					|.code
12083					|1:
12084				} else {
12085					|	GET_ZVAL_TYPE_INFO edx, val_addr
12086					|	IF_NOT_TYPE dl, type, >1
12087					|.cold_code
12088					|1:
12089					|	IF_NOT_TYPE dl, IS_REFERENCE, &res_exit_addr
12090					|	GET_Z_PTR r0, r0
12091					|	add r0, offsetof(zend_reference, val)
12092					|	GET_ZVAL_TYPE_INFO edx, val_addr
12093					|	IF_TYPE dl, type, >1
12094					|	jmp &res_exit_addr
12095					|.code
12096					|1:
12097				}
12098			} else {
12099				if (op1_info & MAY_BE_ARRAY_OF_REF) {
12100					|	ZVAL_DEREF r0, MAY_BE_REF
12101				}
12102				if (type < IS_STRING) {
12103					|	IF_NOT_ZVAL_TYPE val_addr, type, &res_exit_addr
12104				} else {
12105					|	GET_ZVAL_TYPE_INFO edx, val_addr
12106					|	IF_NOT_TYPE dl, type, &res_exit_addr
12107				}
12108			}
12109
12110			|	// ZVAL_COPY
12111			|7:
12112			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_R0, ZREG_R1
12113			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
12114				if (type < IS_STRING) {
12115					if (Z_REG(res_addr) != ZREG_FP ||
12116					    JIT_G(current_frame) == NULL ||
12117					    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
12118						|	SET_ZVAL_TYPE_INFO res_addr, type
12119					}
12120				} else {
12121					|	SET_ZVAL_TYPE_INFO res_addr, edx
12122					if (!result_avoid_refcounting) {
12123						|	TRY_ADDREF res_info, dh, r1
12124					}
12125				}
12126			} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
12127				return 0;
12128			}
12129		} else if (op1_info & MAY_BE_ARRAY_OF_REF) {
12130			|	// ZVAL_COPY_DEREF
12131			|	GET_ZVAL_TYPE_INFO Rd(ZREG_R2), val_addr
12132			if (!zend_jit_zval_copy_deref(Dst, res_addr, val_addr, ZREG_R2)) {
12133				return 0;
12134			}
12135		} else  {
12136			|	// ZVAL_COPY
12137			|	ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_R1, ZREG_R2
12138			|	TRY_ADDREF res_info, ch, r2
12139		}
12140	}
12141	|9: // END
12142
12143#ifdef ZEND_JIT_USE_RC_INFERENCE
12144	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
12145		/* Magic offsetGet() may increase refcount of the key */
12146		op2_info |= MAY_BE_RCN;
12147	}
12148#endif
12149
12150    if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
12151		if ((op2_info & MAY_HAVE_DTOR) && (op2_info & MAY_BE_RC1)) {
12152			may_throw = 1;
12153		}
12154		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12155	}
12156	if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting) {
12157		if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
12158			if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
12159				may_throw = 1;
12160			}
12161			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
12162		}
12163	}
12164
12165	if (may_throw) {
12166		if (!zend_jit_check_exception(Dst)) {
12167			return 0;
12168		}
12169	}
12170
12171	return 1;
12172}
12173
12174static int zend_jit_fetch_dim(dasm_State    **Dst,
12175                              const zend_op  *opline,
12176                              uint32_t        op1_info,
12177                              zend_jit_addr   op1_addr,
12178                              uint32_t        op2_info,
12179                              zend_jit_addr   res_addr,
12180                              uint8_t         dim_type)
12181{
12182	zend_jit_addr op2_addr;
12183	int may_throw = 0;
12184
12185	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
12186
12187	if (opline->opcode == ZEND_FETCH_DIM_RW) {
12188		|	SET_EX_OPLINE opline, r0
12189	}
12190	if (op1_info & MAY_BE_REF) {
12191		may_throw = 1;
12192		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12193		|	IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
12194		|	GET_Z_PTR FCARG2a, FCARG1a
12195		|	IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
12196		|	lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
12197		|	jmp >3
12198		|.cold_code
12199		|2:
12200		if (opline->opcode != ZEND_FETCH_DIM_RW) {
12201			|	SET_EX_OPLINE opline, r0
12202		}
12203		|	EXT_CALL zend_jit_prepare_assign_dim_ref, r0
12204		|	test r0, r0
12205		|	mov FCARG1a, r0
12206		|	jne >1
12207		|	jmp ->exception_handler_undef
12208		|.code
12209		|1:
12210		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12211	}
12212
12213	if (op1_info & MAY_BE_ARRAY) {
12214		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
12215			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
12216		}
12217		|3:
12218		|	SEPARATE_ARRAY op1_addr, op1_info, 1
12219	}
12220	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
12221		if (op1_info & MAY_BE_ARRAY) {
12222			|.cold_code
12223			|7:
12224		}
12225		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
12226			|	CMP_ZVAL_TYPE op1_addr, IS_NULL
12227			|	jg >7
12228		}
12229		if (Z_REG(op1_addr) != ZREG_FP) {
12230			|	mov T1, Ra(Z_REG(op1_addr)) // save
12231		}
12232		if ((op1_info & MAY_BE_UNDEF)
12233		 && opline->opcode == ZEND_FETCH_DIM_RW) {
12234			may_throw = 1;
12235			if (op1_info & MAY_BE_NULL) {
12236				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
12237			}
12238			|	mov FCARG1a, opline->op1.var
12239			|	EXT_CALL zend_jit_undefined_op_helper, r0
12240			|1:
12241		}
12242		|	// ZVAL_ARR(container, zend_new_array(8));
12243		|	EXT_CALL _zend_new_array_0, r0
12244		if (Z_REG(op1_addr) != ZREG_FP) {
12245			|	mov Ra(Z_REG(op1_addr)), T1 // restore
12246		}
12247		|	SET_ZVAL_LVAL op1_addr, r0
12248		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
12249		|	mov FCARG1a, r0
12250		if (op1_info & MAY_BE_ARRAY) {
12251			|	jmp >1
12252			|.code
12253			|1:
12254		}
12255	}
12256
12257	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
12258		|6:
12259		if (opline->op2_type == IS_UNUSED) {
12260			may_throw = 1;
12261			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
12262			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
12263			|	EXT_CALL zend_hash_next_index_insert, r0
12264			|	// if (UNEXPECTED(!var_ptr)) {
12265			|	test r0, r0
12266			|	jz >1
12267			|.cold_code
12268			|1:
12269			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
12270			|	CANNOT_ADD_ELEMENT opline
12271			|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF
12272			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
12273			|	jmp >8
12274			|.code
12275			|	SET_ZVAL_PTR res_addr, r0
12276			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
12277		} else {
12278			uint32_t type;
12279
12280			switch (opline->opcode) {
12281				case ZEND_FETCH_DIM_W:
12282				case ZEND_FETCH_LIST_W:
12283					type = BP_VAR_W;
12284					break;
12285				case ZEND_FETCH_DIM_RW:
12286					may_throw = 1;
12287					type = BP_VAR_RW;
12288					break;
12289				case ZEND_FETCH_DIM_UNSET:
12290					type = BP_VAR_UNSET;
12291					break;
12292				default:
12293					ZEND_UNREACHABLE();
12294			}
12295
12296			if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
12297				may_throw = 1;
12298			}
12299			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, type, op1_info, op2_info, dim_type, NULL, NULL, NULL)) {
12300				return 0;
12301			}
12302
12303			|8:
12304			|	SET_ZVAL_PTR res_addr, r0
12305			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
12306
12307			if (type == BP_VAR_RW || (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
12308				|.cold_code
12309				|9:
12310				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
12311				|	jmp >8
12312				|.code
12313			}
12314		}
12315	}
12316
12317	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
12318		may_throw = 1;
12319		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
12320			|.cold_code
12321			|7:
12322		}
12323
12324		if (opline->opcode != ZEND_FETCH_DIM_RW) {
12325			|	SET_EX_OPLINE opline, r0
12326		}
12327		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12328			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12329		}
12330	    if (opline->op2_type == IS_UNUSED) {
12331			|	xor FCARG2a, FCARG2a
12332		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
12333			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
12334			|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
12335		} else {
12336			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
12337		}
12338		|.if X64
12339			|	LOAD_ZVAL_ADDR CARG3, res_addr
12340		|.else
12341			|	sub r4, 12
12342			|	PUSH_ZVAL_ADDR res_addr, r0
12343		|.endif
12344		switch (opline->opcode) {
12345			case ZEND_FETCH_DIM_W:
12346			case ZEND_FETCH_LIST_W:
12347				|	EXT_CALL zend_jit_fetch_dim_obj_w_helper, r0
12348				break;
12349			case ZEND_FETCH_DIM_RW:
12350				|	EXT_CALL zend_jit_fetch_dim_obj_rw_helper, r0
12351				break;
12352//			case ZEND_FETCH_DIM_UNSET:
12353//				|	EXT_CALL zend_jit_fetch_dim_obj_unset_helper, r0
12354//				break;
12355			default:
12356				ZEND_UNREACHABLE();
12357			}
12358		|.if not(X64)
12359		|	add r4, 12
12360		|.endif
12361
12362		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
12363			|	jmp >8 // END
12364			|.code
12365		}
12366	}
12367
12368#ifdef ZEND_JIT_USE_RC_INFERENCE
12369	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))) {
12370		/* ASSIGN_DIM may increase refcount of the key */
12371		op2_info |= MAY_BE_RCN;
12372	}
12373#endif
12374
12375	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
12376	 && (op2_info & MAY_HAVE_DTOR)
12377	 && (op2_info & MAY_BE_RC1)) {
12378		may_throw = 1;
12379	}
12380	|8:
12381	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12382
12383	if (may_throw) {
12384		if (!zend_jit_check_exception(Dst)) {
12385			return 0;
12386		}
12387	}
12388
12389	return 1;
12390}
12391
12392static int zend_jit_isset_isempty_dim(dasm_State    **Dst,
12393                                      const zend_op  *opline,
12394                                      uint32_t        op1_info,
12395                                      zend_jit_addr   op1_addr,
12396                                      bool       op1_avoid_refcounting,
12397                                      uint32_t        op2_info,
12398                                      uint8_t         dim_type,
12399                                      int             may_throw,
12400                                      uint8_t      smart_branch_opcode,
12401                                      uint32_t        target_label,
12402                                      uint32_t        target_label2,
12403                                      const void     *exit_addr)
12404{
12405	zend_jit_addr op2_addr, res_addr;
12406
12407	// TODO: support for empty() ???
12408	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
12409
12410	op2_addr = OP2_ADDR();
12411	res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12412
12413	if (op1_info & MAY_BE_REF) {
12414		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12415		|	ZVAL_DEREF FCARG1a, op1_info
12416		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12417	}
12418
12419	if (op1_info & MAY_BE_ARRAY) {
12420		const void *found_exit_addr = NULL;
12421		const void *not_found_exit_addr = NULL;
12422
12423		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
12424			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
12425		}
12426		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
12427		if (exit_addr
12428		 && !(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY))
12429		 && !may_throw
12430		 && (!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || op1_avoid_refcounting)
12431		 && (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)))) {
12432			if (smart_branch_opcode == ZEND_JMPNZ) {
12433				found_exit_addr = exit_addr;
12434			} else {
12435				not_found_exit_addr = exit_addr;
12436			}
12437		}
12438		if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_JIT_IS, op1_info, op2_info, dim_type, found_exit_addr, not_found_exit_addr, NULL)) {
12439			return 0;
12440		}
12441
12442		if (found_exit_addr) {
12443			|9:
12444			return 1;
12445		} else if (not_found_exit_addr) {
12446			|8:
12447			return 1;
12448		}
12449	}
12450
12451	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
12452		if (op1_info & MAY_BE_ARRAY) {
12453			|.cold_code
12454			|7:
12455		}
12456
12457		if (op1_info & (MAY_BE_STRING|MAY_BE_OBJECT)) {
12458			|	SET_EX_OPLINE opline, r0
12459		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12460				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12461			}
12462			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
12463				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
12464				|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
12465			} else {
12466				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
12467			}
12468			|	EXT_CALL zend_jit_isset_dim_helper, r0
12469			|	test r0, r0
12470			|	jz >9
12471			if (op1_info & MAY_BE_ARRAY) {
12472				|	jmp >8
12473				|.code
12474			}
12475		} else {
12476			if (op2_info & MAY_BE_UNDEF) {
12477				if (op2_info & MAY_BE_ANY) {
12478					|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1
12479				}
12480				|	SET_EX_OPLINE opline, r0
12481				|	mov FCARG1d, opline->op2.var
12482				|	EXT_CALL zend_jit_undefined_op_helper, r0
12483				|1:
12484			}
12485			if (op1_info & MAY_BE_ARRAY) {
12486				|	jmp >9
12487				|.code
12488			}
12489		}
12490	}
12491
12492#ifdef ZEND_JIT_USE_RC_INFERENCE
12493	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
12494		/* Magic offsetExists() may increase refcount of the key */
12495		op2_info |= MAY_BE_RCN;
12496	}
12497#endif
12498
12499	if (op1_info & (MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_OBJECT)) {
12500		|8:
12501		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12502		if (!op1_avoid_refcounting) {
12503			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
12504		}
12505		if (may_throw) {
12506			if (!zend_jit_check_exception_undef_result(Dst, opline)) {
12507				return 0;
12508			}
12509		}
12510		if (!(opline->extended_value & ZEND_ISEMPTY)) {
12511			if (exit_addr) {
12512				if (smart_branch_opcode == ZEND_JMPNZ) {
12513					|	jmp &exit_addr
12514				} else {
12515					|	jmp >8
12516				}
12517			} else if (smart_branch_opcode) {
12518				if (smart_branch_opcode == ZEND_JMPZ) {
12519					|	jmp =>target_label2
12520				} else if (smart_branch_opcode == ZEND_JMPNZ) {
12521					|	jmp =>target_label
12522				} else {
12523					ZEND_UNREACHABLE();
12524				}
12525			} else {
12526				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
12527				|	jmp >8
12528			}
12529		} else {
12530			|	NIY // TODO: support for empty()
12531		}
12532	}
12533
12534	|9: // not found
12535	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12536	if (!op1_avoid_refcounting) {
12537		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
12538	}
12539	if (may_throw) {
12540		if (!zend_jit_check_exception_undef_result(Dst, opline)) {
12541			return 0;
12542		}
12543	}
12544	if (!(opline->extended_value & ZEND_ISEMPTY)) {
12545		if (exit_addr) {
12546			if (smart_branch_opcode == ZEND_JMPZ) {
12547				|	jmp &exit_addr
12548			}
12549		} else if (smart_branch_opcode) {
12550			if (smart_branch_opcode == ZEND_JMPZ) {
12551				|	jmp =>target_label
12552			} else if (smart_branch_opcode == ZEND_JMPNZ) {
12553			} else {
12554				ZEND_UNREACHABLE();
12555			}
12556		} else {
12557			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
12558		}
12559	} else {
12560		|	NIY // TODO: support for empty()
12561	}
12562
12563	|8:
12564
12565	return 1;
12566}
12567
12568static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
12569{
12570	zend_jit_addr op1_addr = OP1_ADDR();
12571	zend_string *varname = Z_STR_P(RT_CONSTANT(opline, opline->op2));
12572
12573	|	// idx = (uint32_t)(uintptr_t)CACHED_PTR(opline->extended_value) - 1;
12574	|	mov FCARG2a, EX->run_time_cache
12575	|	mov r0, aword [FCARG2a + opline->extended_value]
12576	|	sub r0, 1
12577	|	// if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket)))
12578	|	MEM_LOAD_ZTS ecx, dword, executor_globals, symbol_table.nNumUsed, r1
12579	|.if X64
12580		|	shl r1, 5
12581	|.else
12582		|	imul r1, sizeof(Bucket)
12583	|.endif
12584	|	cmp r0, r1
12585	|	jae >9
12586	|	// Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx);
12587	|	MEM_LOAD_OP_ZTS add, r0, aword, executor_globals, symbol_table.arData, r1
12588	|	IF_NOT_Z_TYPE r0, IS_REFERENCE, >9
12589	|	// (EXPECTED(p->key == varname))
12590	|	ADDR_CMP aword [r0 + offsetof(Bucket, key)], varname, r1
12591	|	jne >9
12592	|	GET_Z_PTR r0, r0
12593	|	GC_ADDREF r0
12594	|1:
12595	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
12596		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
12597			|	// if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr)))
12598			|	IF_ZVAL_REFCOUNTED op1_addr, >2
12599			|.cold_code
12600			|2:
12601		}
12602		|	// zend_refcounted *garbage = Z_COUNTED_P(variable_ptr);
12603		|	GET_ZVAL_PTR FCARG1a, op1_addr
12604		|	// ZVAL_REF(variable_ptr, ref)
12605		|	SET_ZVAL_PTR op1_addr, r0
12606		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX
12607		|	// if (GC_DELREF(garbage) == 0)
12608		|	GC_DELREF FCARG1a
12609		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
12610			|	jnz >3
12611		} else {
12612			|	jnz >5
12613		}
12614		|	ZVAL_DTOR_FUNC op1_info, opline
12615		|	jmp >5
12616		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
12617			|3:
12618			|	// GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr)
12619			|	IF_GC_MAY_NOT_LEAK FCARG1a, >5
12620			|	EXT_CALL gc_possible_root, r1
12621			|	jmp >5
12622		}
12623		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
12624			|.code
12625		}
12626	}
12627
12628	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
12629		|	// ZVAL_REF(variable_ptr, ref)
12630		|	SET_ZVAL_PTR op1_addr, r0
12631		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX
12632	}
12633	|5:
12634	//END of handler
12635
12636	|.cold_code
12637	|9:
12638	|	LOAD_ADDR FCARG1a, (ptrdiff_t)varname
12639	if (opline->extended_value) {
12640		|	add FCARG2a, opline->extended_value
12641	}
12642	|	EXT_CALL zend_jit_fetch_global_helper, r0
12643	|	jmp <1
12644	|.code
12645
12646	return 1;
12647}
12648
12649static int zend_jit_verify_arg_type(dasm_State **Dst, const zend_op *opline, zend_arg_info *arg_info, bool check_exception)
12650{
12651	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12652	bool in_cold = 0;
12653	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
12654	zend_reg tmp_reg = (type_mask == 0 || is_power_of_two(type_mask)) ? ZREG_FCARG1 : ZREG_R0;
12655
12656	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
12657	 && JIT_G(current_frame)
12658	 && JIT_G(current_frame)->prev) {
12659		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
12660		uint8_t type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var));
12661
12662		if (type != IS_UNKNOWN && (type_mask & (1u << type))) {
12663			return 1;
12664		}
12665	}
12666
12667	if (ZEND_ARG_SEND_MODE(arg_info)) {
12668		if (opline->opcode == ZEND_RECV_INIT) {
12669			|	LOAD_ZVAL_ADDR Ra(tmp_reg), res_addr
12670			|	ZVAL_DEREF Ra(tmp_reg), MAY_BE_REF
12671			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, 0);
12672		} else {
12673			|	GET_ZVAL_PTR Ra(tmp_reg), res_addr
12674			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, offsetof(zend_reference, val));
12675		}
12676	}
12677
12678	if (type_mask != 0) {
12679		if (is_power_of_two(type_mask)) {
12680			uint32_t type_code = concrete_type(type_mask);
12681			|	IF_NOT_ZVAL_TYPE res_addr, type_code, >1
12682		} else {
12683			|	mov edx, 1
12684			|	mov cl, byte [Ra(Z_REG(res_addr))+Z_OFFSET(res_addr)+offsetof(zval, u1.v.type)]
12685			|	shl edx, cl
12686			|	test edx, type_mask
12687			|	je >1
12688		}
12689
12690		|.cold_code
12691		|1:
12692
12693		in_cold = 1;
12694	}
12695
12696	if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
12697		|	LOAD_ZVAL_ADDR FCARG1a, res_addr
12698	}
12699	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12700		|	SET_EX_OPLINE opline, r0
12701	} else {
12702		|	ADDR_STORE aword EX->opline, opline, r0
12703	}
12704	|	LOAD_ADDR FCARG2a, (ptrdiff_t)arg_info
12705	|	EXT_CALL zend_jit_verify_arg_slow, r0
12706
12707	if (check_exception) {
12708		|	test al, al
12709		if (in_cold) {
12710			|	jnz >1
12711			|	jmp ->exception_handler
12712			|.code
12713			|1:
12714		} else {
12715			|	jz ->exception_handler
12716		}
12717	} else if (in_cold) {
12718		|	jmp >1
12719		|.code
12720		|1:
12721	}
12722
12723	return 1;
12724}
12725
12726static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array)
12727{
12728	uint32_t arg_num = opline->op1.num;
12729	zend_arg_info *arg_info = NULL;
12730
12731	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
12732		if (EXPECTED(arg_num <= op_array->num_args)) {
12733			arg_info = &op_array->arg_info[arg_num-1];
12734		} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
12735			arg_info = &op_array->arg_info[op_array->num_args];
12736		}
12737		if (arg_info && !ZEND_TYPE_IS_SET(arg_info->type)) {
12738			arg_info = NULL;
12739		}
12740	}
12741
12742	if (arg_info || (opline+1)->opcode != ZEND_RECV) {
12743		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12744			if (!JIT_G(current_frame) ||
12745			    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) < 0 ||
12746			    arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) {
12747				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12748				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12749
12750				if (!exit_addr) {
12751					return 0;
12752				}
12753				|	cmp dword EX->This.u2.num_args, arg_num
12754				|	jb &exit_addr
12755			}
12756		} else {
12757			|	cmp dword EX->This.u2.num_args, arg_num
12758			|	jb >1
12759			|.cold_code
12760			|1:
12761			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12762				|	SET_EX_OPLINE opline, r0
12763			} else {
12764				|	ADDR_STORE aword EX->opline, opline, r0
12765			}
12766			|	mov FCARG1a, FP
12767			|	EXT_CALL zend_missing_arg_error, r0
12768			|	jmp ->exception_handler
12769			|.code
12770		}
12771	}
12772
12773	if (arg_info) {
12774		if (!zend_jit_verify_arg_type(Dst, opline, arg_info, 1)) {
12775			return 0;
12776		}
12777	}
12778
12779	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
12780		if ((opline+1)->opcode != ZEND_RECV && (opline+1)->opcode != ZEND_RECV_INIT) {
12781			|	LOAD_IP_ADDR (opline + 1)
12782			zend_jit_set_last_valid_opline(opline + 1);
12783		}
12784	}
12785
12786	return 1;
12787}
12788
12789static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool is_last, int may_throw)
12790{
12791	uint32_t arg_num = opline->op1.num;
12792	zval *zv = RT_CONSTANT(opline, opline->op2);
12793	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12794
12795	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
12796	 && JIT_G(current_frame)
12797	 && TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) >= 0) {
12798		if (arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) {
12799			|	ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_R0
12800			if (Z_REFCOUNTED_P(zv)) {
12801				|	ADDREF_CONST zv, r0
12802			}
12803		}
12804	} else {
12805		if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
12806		    (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
12807			|	cmp dword EX->This.u2.num_args, arg_num
12808			|	jae >5
12809		}
12810		|	ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_R0
12811		if (Z_REFCOUNTED_P(zv)) {
12812			|	ADDREF_CONST zv, r0
12813		}
12814	}
12815
12816	if (Z_CONSTANT_P(zv)) {
12817		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12818			|	SET_EX_OPLINE opline, r0
12819		} else {
12820			|	ADDR_STORE aword EX->opline, opline, r0
12821		}
12822		|	LOAD_ZVAL_ADDR FCARG1a, res_addr
12823		|	mov r0, EX->func
12824		|	mov FCARG2a, [r0 + offsetof(zend_op_array, scope)]
12825		|	.if X64
12826		|		EXT_CALL zval_update_constant_ex, r0
12827		|	.else
12828		|		EXT_CALL zval_update_constant_ex, r0
12829		|	.endif
12830		|	test al, al
12831		|	jnz >1
12832		|.cold_code
12833		|1:
12834		|	ZVAL_PTR_DTOR res_addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 1, 0, opline
12835		|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF
12836		|	jmp ->exception_handler
12837		|.code
12838	}
12839
12840	|5:
12841
12842	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
12843		do {
12844			zend_arg_info *arg_info;
12845
12846			if (arg_num <= op_array->num_args) {
12847				arg_info = &op_array->arg_info[arg_num-1];
12848			} else if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
12849				arg_info = &op_array->arg_info[op_array->num_args];
12850			} else {
12851				break;
12852			}
12853			if (!ZEND_TYPE_IS_SET(arg_info->type)) {
12854				break;
12855			}
12856			if (!zend_jit_verify_arg_type(Dst, opline, arg_info, may_throw)) {
12857				return 0;
12858			}
12859		} while (0);
12860	}
12861
12862	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
12863		if (is_last) {
12864			|	LOAD_IP_ADDR (opline + 1)
12865			zend_jit_set_last_valid_opline(opline + 1);
12866		}
12867	}
12868
12869	return 1;
12870}
12871
12872static int zend_jit_class_guard(dasm_State **Dst, const zend_op *opline, zend_class_entry *ce)
12873{
12874	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
12875	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12876
12877	if (!exit_addr) {
12878		return 0;
12879	}
12880
12881	|.if X64
12882	||	if (!IS_SIGNED_32BIT(ce)) {
12883	|		mov64 r0, ((ptrdiff_t)ce)
12884	|		cmp aword [FCARG1a + offsetof(zend_object, ce)], r0
12885	||	} else {
12886	|		cmp aword [FCARG1a + offsetof(zend_object, ce)], ce
12887	||	}
12888	|.else
12889	|	cmp aword [FCARG1a + offsetof(zend_object, ce)], ce
12890	|.endif
12891	|	jne &exit_addr
12892
12893	return 1;
12894}
12895
12896static int zend_jit_fetch_obj(dasm_State          **Dst,
12897                              const zend_op        *opline,
12898                              const zend_op_array  *op_array,
12899                              zend_ssa             *ssa,
12900                              const zend_ssa_op    *ssa_op,
12901                              uint32_t              op1_info,
12902                              zend_jit_addr         op1_addr,
12903                              bool                  op1_indirect,
12904                              zend_class_entry     *ce,
12905                              bool                  ce_is_instanceof,
12906                              bool                  on_this,
12907                              bool                  delayed_fetch_this,
12908                              bool                  op1_avoid_refcounting,
12909                              zend_class_entry     *trace_ce,
12910                              uint8_t               prop_type,
12911                              int                   may_throw)
12912{
12913	zval *member;
12914	zend_property_info *prop_info;
12915	bool may_be_dynamic = 1;
12916	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12917	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
12918	zend_jit_addr prop_addr;
12919	uint32_t res_info = RES_INFO();
12920	bool type_loaded = 0;
12921
12922	ZEND_ASSERT(opline->op2_type == IS_CONST);
12923	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
12924
12925	member = RT_CONSTANT(opline, opline->op2);
12926	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
12927	prop_info = zend_get_known_property_info(op_array, ce, Z_STR_P(member), on_this, op_array->filename);
12928
12929	if (on_this) {
12930		|	GET_ZVAL_PTR FCARG1a, this_addr
12931	} else {
12932		if (opline->op1_type == IS_VAR
12933		 && opline->opcode == ZEND_FETCH_OBJ_W
12934		 && (op1_info & MAY_BE_INDIRECT)
12935		 && Z_REG(op1_addr) == ZREG_FP) {
12936			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12937			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
12938			|	GET_Z_PTR FCARG1a, FCARG1a
12939			|1:
12940			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12941		}
12942		if (op1_info & MAY_BE_REF) {
12943			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12944				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12945			}
12946			|	ZVAL_DEREF FCARG1a, op1_info
12947			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12948		}
12949		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
12950			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12951				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12952				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12953
12954				if (!exit_addr) {
12955					return 0;
12956				}
12957				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
12958			} else {
12959				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >7
12960			}
12961		}
12962		|	GET_ZVAL_PTR FCARG1a, op1_addr
12963	}
12964
12965	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
12966		prop_info = zend_get_known_property_info(op_array, trace_ce, Z_STR_P(member), on_this, op_array->filename);
12967		if (prop_info) {
12968			ce = trace_ce;
12969			ce_is_instanceof = 0;
12970			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
12971				if (on_this && JIT_G(current_frame)
12972				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
12973					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
12974				} else if (zend_jit_class_guard(Dst, opline, ce)) {
12975					if (on_this && JIT_G(current_frame)) {
12976						JIT_G(current_frame)->ce = ce;
12977						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
12978					}
12979				} else {
12980					return 0;
12981				}
12982				if (ssa->var_info && ssa_op->op1_use >= 0) {
12983					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
12984					ssa->var_info[ssa_op->op1_use].ce = ce;
12985					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
12986				}
12987			}
12988		}
12989	}
12990
12991	if (!prop_info) {
12992		|	mov r0, EX->run_time_cache
12993		|	mov r2, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS)]
12994		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
12995		|	jne >5
12996		|	mov r0, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*)]
12997		may_be_dynamic = zend_may_be_dynamic_property(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array);
12998		if (may_be_dynamic) {
12999			|	test r0, r0
13000			if (opline->opcode == ZEND_FETCH_OBJ_W) {
13001				|	jl >5
13002			} else {
13003				|	jl >8 // dynamic property
13004			}
13005		}
13006		|	mov edx, dword [FCARG1a + r0 + 8]
13007		|	IF_UNDEF dl, >5
13008		|	add FCARG1a, r0
13009		type_loaded = 1;
13010		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13011		if (opline->opcode == ZEND_FETCH_OBJ_W
13012		 && (!ce ||	ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT)))) {
13013			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
13014
13015			|	mov r0, EX->run_time_cache
13016			|	mov FCARG2a, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2]
13017			|	test FCARG2a, FCARG2a
13018			|	jnz >1
13019			|.cold_code
13020			|1:
13021			|	test dword [FCARG2a + offsetof(zend_property_info, flags)], ZEND_ACC_READONLY
13022			if (flags) {
13023				|	jz >3
13024			} else {
13025				|	jz >4
13026			}
13027			|	IF_NOT_Z_TYPE FCARG1a, IS_OBJECT, >2
13028			|	GET_Z_PTR r0, FCARG1a
13029			|	GC_ADDREF r0
13030			|	SET_ZVAL_PTR res_addr, r0
13031			|	SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX
13032			|	jmp >9
13033			|2:
13034			|	mov eax, dword [FCARG1a + offsetof(zval, u2.extra)]
13035			|	test eax, IS_PROP_REINITABLE
13036			|	jz >6
13037			|	and eax, ~IS_PROP_REINITABLE
13038			|	mov dword [FCARG1a + offsetof(zval, u2.extra)], eax
13039			if (flags) {
13040				|	jmp >3
13041			} else {
13042				|	jmp >4
13043			}
13044			|6:
13045			|	mov FCARG1a, FCARG2a
13046			|	SET_EX_OPLINE opline, r0
13047			|	EXT_CALL zend_readonly_property_modification_error, r0
13048			|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR
13049			|	jmp >9
13050			|3:
13051			if (flags == ZEND_FETCH_DIM_WRITE) {
13052				|	SET_EX_OPLINE opline, r0
13053				|	EXT_CALL zend_jit_check_array_promotion, r0
13054				|	jmp >9
13055			} else if (flags == ZEND_FETCH_REF) {
13056				|.if X64
13057					|	LOAD_ZVAL_ADDR CARG3, res_addr
13058				|.else
13059					|	sub r4, 12
13060					|	PUSH_ZVAL_ADDR res_addr, r0
13061				|.endif
13062				|	EXT_CALL zend_jit_create_typed_ref, r0
13063				|.if not(X64)
13064				|	add r4, 12
13065				|.endif
13066				|	jmp >9
13067			} else {
13068				ZEND_ASSERT(flags == 0);
13069			}
13070			|.code
13071			|4:
13072		}
13073	} else {
13074		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
13075		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13076			if (opline->opcode == ZEND_FETCH_OBJ_W || !(res_info & MAY_BE_GUARD) || !JIT_G(current_frame)) {
13077				/* perform IS_UNDEF check only after result type guard (during deoptimization) */
13078				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13079				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13080
13081				if (!exit_addr) {
13082					return 0;
13083				}
13084				type_loaded = 1;
13085				|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13086				|	IF_UNDEF dl, &exit_addr
13087			}
13088		} else {
13089			type_loaded = 1;
13090			|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13091			|	IF_UNDEF dl, >5
13092		}
13093		if (opline->opcode == ZEND_FETCH_OBJ_W && (prop_info->flags & ZEND_ACC_READONLY)) {
13094			if (!type_loaded) {
13095				type_loaded = 1;
13096				|	mov edx, dword [FCARG1a + prop_info->offset + offsetof(zval, u1.type_info)]
13097			}
13098			|	IF_NOT_TYPE dl, IS_OBJECT, >4
13099			|	GET_ZVAL_PTR r0, prop_addr
13100			|	GC_ADDREF r0
13101			|	SET_ZVAL_PTR res_addr, r0
13102			|	SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX
13103			|	jmp >9
13104			|.cold_code
13105			|4:
13106			|	mov eax, dword [FCARG1a + prop_info->offset + offsetof(zval, u2.extra)]
13107			|	test eax, IS_PROP_REINITABLE
13108			|	jz >6
13109			|	and eax, ~IS_PROP_REINITABLE
13110			|	mov dword [FCARG1a + prop_info->offset + offsetof(zval, u2.extra)], eax
13111			|	jmp >4
13112			|6:
13113			|	LOAD_ADDR FCARG1a, prop_info
13114			|	SET_EX_OPLINE opline, r0
13115			|	EXT_CALL zend_readonly_property_modification_error, r0
13116			|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR
13117			|	jmp >9
13118			|.code
13119			|4:
13120		}
13121		if (opline->opcode == ZEND_FETCH_OBJ_W
13122		 && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS)
13123		 && ZEND_TYPE_IS_SET(prop_info->type)) {
13124			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
13125
13126			if (flags == ZEND_FETCH_DIM_WRITE) {
13127				if ((ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_ARRAY) == 0) {
13128					if (!type_loaded) {
13129						type_loaded = 1;
13130						|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13131					}
13132					|	cmp dl, IS_FALSE
13133					|	jle >1
13134					|.cold_code
13135					|1:
13136					if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13137						|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13138					}
13139					|	LOAD_ADDR FCARG2a, prop_info
13140					|	SET_EX_OPLINE opline, r0
13141					|	EXT_CALL zend_jit_check_array_promotion, r0
13142					|	jmp >9
13143					|.code
13144				}
13145			} else if (flags == ZEND_FETCH_REF) {
13146				if (!type_loaded) {
13147					type_loaded = 1;
13148					|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13149				}
13150				|	IF_TYPE dl, IS_REFERENCE, >1
13151				if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
13152					|	LOAD_ADDR FCARG2a, prop_info
13153				} else {
13154					int prop_info_offset =
13155						(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
13156
13157					|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
13158					|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
13159					|	mov FCARG2a, aword[r0 + prop_info_offset]
13160				}
13161				if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13162					|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13163				}
13164				|.if X64
13165					|	LOAD_ZVAL_ADDR CARG3, res_addr
13166				|.else
13167					|	sub r4, 12
13168					|	PUSH_ZVAL_ADDR res_addr, r0
13169				|.endif
13170				|	EXT_CALL zend_jit_create_typed_ref, r0
13171				|.if not(X64)
13172				|	add r4, 12
13173				|.endif
13174				|	jmp >9
13175				|1:
13176			} else {
13177				ZEND_UNREACHABLE();
13178			}
13179		}
13180	}
13181	if (opline->opcode == ZEND_FETCH_OBJ_W) {
13182		if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13183			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13184		}
13185		|	SET_ZVAL_PTR res_addr, FCARG1a
13186		|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
13187		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && prop_info) {
13188			ssa->var_info[ssa_op->result_def].indirect_reference = 1;
13189		}
13190	} else {
13191		bool result_avoid_refcounting = 0;
13192
13193		if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) {
13194			uint32_t flags = 0;
13195			uint32_t old_info;
13196			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
13197			int32_t exit_point;
13198			const void *exit_addr;
13199			uint8_t type;
13200			zend_jit_addr val_addr = prop_addr;
13201
13202			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
13203			 && !delayed_fetch_this
13204			 && !op1_avoid_refcounting) {
13205				flags = ZEND_JIT_EXIT_FREE_OP1;
13206			}
13207
13208			if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
13209			 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
13210			 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
13211			 && (ssa_op+1)->op1_use == ssa_op->result_def
13212			 && zend_jit_may_avoid_refcounting(opline+1, res_info)) {
13213				result_avoid_refcounting = 1;
13214				ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
13215			}
13216
13217			type = concrete_type(res_info);
13218
13219			if (prop_type != IS_UNKNOWN
13220			 && prop_type != IS_UNDEF
13221			 && prop_type != IS_REFERENCE
13222			 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT) {
13223				exit_point = zend_jit_trace_get_exit_point(opline, 0);
13224				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13225				if (!exit_addr) {
13226					return 0;
13227				}
13228			} else {
13229				val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
13230				|	LOAD_ZVAL_ADDR r0, prop_addr
13231				if (op1_avoid_refcounting) {
13232					SET_STACK_REG(JIT_G(current_frame)->stack,
13233						EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
13234				}
13235				old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
13236				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
13237				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
13238				exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
13239					SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
13240				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13241				if (!exit_addr) {
13242					return 0;
13243				}
13244
13245				if (!type_loaded) {
13246					type_loaded = 1;
13247					|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13248				}
13249				|	// ZVAL_DEREF()
13250				|	IF_NOT_TYPE dl, IS_REFERENCE, >1
13251				|	GET_Z_PTR r0, r0
13252				|	add r0, offsetof(zend_reference, val)
13253				|	GET_ZVAL_TYPE_INFO edx, val_addr
13254			}
13255			res_info &= ~MAY_BE_GUARD;
13256			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
13257			if (type < IS_STRING) {
13258				|1:
13259				if (type_loaded) {
13260					|	IF_NOT_TYPE dl, type, &exit_addr
13261				} else {
13262					|	IF_NOT_ZVAL_TYPE val_addr, type, &exit_addr
13263				}
13264			} else {
13265				if (!type_loaded) {
13266					type_loaded = 1;
13267					|	GET_ZVAL_TYPE_INFO edx, val_addr
13268				}
13269				|1:
13270				|	IF_NOT_TYPE dl, type, &exit_addr
13271			}
13272			|	// ZVAL_COPY
13273			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_R0, ZREG_R1
13274			if (type < IS_STRING) {
13275				if (Z_REG(res_addr) != ZREG_FP ||
13276				    JIT_G(current_frame) == NULL ||
13277				    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
13278					|	SET_ZVAL_TYPE_INFO res_addr, type
13279				}
13280			} else {
13281				|	SET_ZVAL_TYPE_INFO res_addr, edx
13282				if (!result_avoid_refcounting) {
13283					|	TRY_ADDREF res_info, dh, r1
13284				}
13285			}
13286		} else {
13287			if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_R2)) {
13288				return 0;
13289			}
13290		}
13291	}
13292
13293	if (op1_avoid_refcounting) {
13294		SET_STACK_REG(JIT_G(current_frame)->stack,
13295			EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
13296	}
13297
13298	|.cold_code
13299
13300	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || !prop_info) {
13301		|5:
13302		|	SET_EX_OPLINE opline, r0
13303		if (opline->opcode == ZEND_FETCH_OBJ_W) {
13304			|	EXT_CALL zend_jit_fetch_obj_w_slow, r0
13305		} else if (opline->opcode != ZEND_FETCH_OBJ_IS) {
13306			|	EXT_CALL zend_jit_fetch_obj_r_slow, r0
13307		} else {
13308			|	EXT_CALL zend_jit_fetch_obj_is_slow, r0
13309		}
13310		|	jmp >9
13311	}
13312
13313	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)- MAY_BE_OBJECT)) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
13314		|7:
13315		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
13316			|	SET_EX_OPLINE opline, r0
13317			if (opline->opcode != ZEND_FETCH_OBJ_W
13318			 && (op1_info & MAY_BE_UNDEF)) {
13319				zend_jit_addr orig_op1_addr = OP1_ADDR();
13320
13321				if (op1_info & MAY_BE_ANY) {
13322					|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
13323				}
13324				|	mov FCARG1d, opline->op1.var
13325				|	EXT_CALL zend_jit_undefined_op_helper, r0
13326				|1:
13327				|	LOAD_ZVAL_ADDR FCARG1a, orig_op1_addr
13328			} else if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13329				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13330			}
13331			|	LOAD_ADDR FCARG2a, Z_STRVAL_P(member)
13332			if (opline->opcode == ZEND_FETCH_OBJ_W) {
13333				|	EXT_CALL zend_jit_invalid_property_write, r0
13334				|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR
13335			} else {
13336				|	EXT_CALL zend_jit_invalid_property_read, r0
13337				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
13338			}
13339			|	jmp >9
13340		} else {
13341			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
13342			|	jmp >9
13343		}
13344	}
13345
13346	if (!prop_info
13347	 && may_be_dynamic
13348	 && opline->opcode != ZEND_FETCH_OBJ_W) {
13349		|8:
13350		|	mov FCARG2a, r0
13351		|	SET_EX_OPLINE opline, r0
13352		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
13353			|	EXT_CALL zend_jit_fetch_obj_r_dynamic, r0
13354		} else {
13355			|	EXT_CALL zend_jit_fetch_obj_is_dynamic, r0
13356		}
13357		|	jmp >9
13358	}
13359
13360	|.code;
13361	|9: // END
13362	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
13363		if (opline->op1_type == IS_VAR
13364		 && opline->opcode == ZEND_FETCH_OBJ_W
13365		 && (op1_info & MAY_BE_RC1)) {
13366			zend_jit_addr orig_op1_addr = OP1_ADDR();
13367
13368			|	IF_NOT_ZVAL_REFCOUNTED orig_op1_addr, >1
13369			|	GET_ZVAL_PTR FCARG1a, orig_op1_addr
13370			|	GC_DELREF FCARG1a
13371			|	jnz >1
13372			|	SET_EX_OPLINE opline, r0
13373			|	EXT_CALL zend_jit_extract_helper, r0
13374			|1:
13375		} else if (!op1_avoid_refcounting) {
13376			if (on_this) {
13377				op1_info &= ~MAY_BE_RC1;
13378			}
13379			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
13380		}
13381	}
13382
13383	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
13384	 && prop_info
13385	 && (opline->opcode != ZEND_FETCH_OBJ_W ||
13386	     !(opline->extended_value & ZEND_FETCH_OBJ_FLAGS) ||
13387	     !ZEND_TYPE_IS_SET(prop_info->type))
13388	 && (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || on_this || op1_indirect)) {
13389		may_throw = 0;
13390	}
13391
13392	if (may_throw) {
13393		if (!zend_jit_check_exception(Dst)) {
13394			return 0;
13395		}
13396	}
13397
13398	return 1;
13399}
13400
13401static int zend_jit_incdec_obj(dasm_State          **Dst,
13402                               const zend_op        *opline,
13403                               const zend_op_array  *op_array,
13404                               zend_ssa             *ssa,
13405                               const zend_ssa_op    *ssa_op,
13406                               uint32_t              op1_info,
13407                               zend_jit_addr         op1_addr,
13408                               bool                  op1_indirect,
13409                               zend_class_entry     *ce,
13410                               bool                  ce_is_instanceof,
13411                               bool                  on_this,
13412                               bool                  delayed_fetch_this,
13413                               zend_class_entry     *trace_ce,
13414                               uint8_t               prop_type)
13415{
13416	zval *member;
13417	zend_string *name;
13418	zend_property_info *prop_info;
13419	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
13420	zend_jit_addr res_addr = 0;
13421	zend_jit_addr prop_addr;
13422	bool needs_slow_path = 0;
13423	bool use_prop_guard = 0;
13424	bool may_throw = 0;
13425	uint32_t res_info = (opline->result_type != IS_UNDEF) ? RES_INFO() : 0;
13426
13427	ZEND_ASSERT(opline->op2_type == IS_CONST);
13428	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
13429
13430	if (opline->result_type != IS_UNUSED) {
13431		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
13432	}
13433
13434	member = RT_CONSTANT(opline, opline->op2);
13435	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
13436	name = Z_STR_P(member);
13437	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
13438
13439	if (on_this) {
13440		|	GET_ZVAL_PTR FCARG1a, this_addr
13441	} else {
13442		if (opline->op1_type == IS_VAR
13443		 && (op1_info & MAY_BE_INDIRECT)
13444		 && Z_REG(op1_addr) == ZREG_FP) {
13445			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13446			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
13447			|	GET_Z_PTR FCARG1a, FCARG1a
13448			|1:
13449			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13450		}
13451		if (op1_info & MAY_BE_REF) {
13452			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13453				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13454			}
13455			|	ZVAL_DEREF FCARG1a, op1_info
13456			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13457		}
13458		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
13459			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13460				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13461				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13462
13463				if (!exit_addr) {
13464					return 0;
13465				}
13466				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
13467			} else {
13468				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
13469				|.cold_code
13470				|1:
13471				|	SET_EX_OPLINE opline, r0
13472				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13473					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13474				}
13475				|	LOAD_ADDR FCARG2a, ZSTR_VAL(name)
13476				|	EXT_CALL zend_jit_invalid_property_incdec, r0
13477				|	jmp ->exception_handler
13478				|.code
13479			}
13480		}
13481		|	GET_ZVAL_PTR FCARG1a, op1_addr
13482	}
13483
13484	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
13485		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
13486		if (prop_info) {
13487			ce = trace_ce;
13488			ce_is_instanceof = 0;
13489			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
13490				if (on_this && JIT_G(current_frame)
13491				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
13492					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
13493				} else if (zend_jit_class_guard(Dst, opline, ce)) {
13494					if (on_this && JIT_G(current_frame)) {
13495						JIT_G(current_frame)->ce = ce;
13496						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
13497					}
13498				} else {
13499					return 0;
13500				}
13501				if (ssa->var_info && ssa_op->op1_use >= 0) {
13502					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
13503					ssa->var_info[ssa_op->op1_use].ce = ce;
13504					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
13505				}
13506				if (ssa->var_info && ssa_op->op1_def >= 0) {
13507					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
13508					ssa->var_info[ssa_op->op1_def].ce = ce;
13509					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
13510				}
13511			}
13512		}
13513	}
13514
13515	use_prop_guard = (prop_type != IS_UNKNOWN
13516		&& prop_type != IS_UNDEF
13517		&& prop_type != IS_REFERENCE
13518		&& (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT);
13519
13520	if (!prop_info) {
13521		needs_slow_path = 1;
13522
13523		|	mov r0, EX->run_time_cache
13524		|	mov r2, aword [r0 + opline->extended_value]
13525		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
13526		|	jne >7
13527		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
13528			|	cmp aword [r0 + opline->extended_value + sizeof(void*) * 2], 0
13529			|	jnz >7
13530		}
13531		|	mov r0, aword [r0 + opline->extended_value + sizeof(void*)]
13532		|	test r0, r0
13533		|	jl >7
13534		if (!use_prop_guard) {
13535			|	IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >7
13536		}
13537		|	add FCARG1a, r0
13538		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13539	} else {
13540		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
13541		if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) {
13542			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13543				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13544				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13545
13546				if (!exit_addr) {
13547					return 0;
13548				}
13549				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr
13550			} else {
13551				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >7
13552				needs_slow_path = 1;
13553			}
13554		}
13555		if (ZEND_TYPE_IS_SET(prop_info->type)) {
13556			may_throw = 1;
13557			|	SET_EX_OPLINE opline, r0
13558			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
13559				|	LOAD_ADDR FCARG2a, prop_info
13560			} else {
13561				int prop_info_offset =
13562					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
13563
13564				|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
13565				|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
13566				|	mov FCARG2a, aword[r0 + prop_info_offset]
13567			}
13568			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13569			if (opline->result_type == IS_UNUSED) {
13570				switch (opline->opcode) {
13571					case ZEND_PRE_INC_OBJ:
13572					case ZEND_POST_INC_OBJ:
13573						|	EXT_CALL zend_jit_inc_typed_prop, r0
13574						break;
13575					case ZEND_PRE_DEC_OBJ:
13576					case ZEND_POST_DEC_OBJ:
13577						|	EXT_CALL zend_jit_dec_typed_prop, r0
13578						break;
13579					default:
13580						ZEND_UNREACHABLE();
13581				}
13582			} else {
13583				|.if X64
13584					|	LOAD_ZVAL_ADDR CARG3, res_addr
13585				|.else
13586					|	sub r4, 12
13587					|	PUSH_ZVAL_ADDR res_addr, r0
13588				|.endif
13589				switch (opline->opcode) {
13590					case ZEND_PRE_INC_OBJ:
13591						|	EXT_CALL zend_jit_pre_inc_typed_prop, r0
13592						break;
13593					case ZEND_PRE_DEC_OBJ:
13594						|	EXT_CALL zend_jit_pre_dec_typed_prop, r0
13595						break;
13596					case ZEND_POST_INC_OBJ:
13597						|	EXT_CALL zend_jit_post_inc_typed_prop, r0
13598						break;
13599					case ZEND_POST_DEC_OBJ:
13600						|	EXT_CALL zend_jit_post_dec_typed_prop, r0
13601						break;
13602					default:
13603						ZEND_UNREACHABLE();
13604				}
13605				|.if not(X64)
13606					|	add r4, 12
13607				|.endif
13608			}
13609		}
13610	}
13611
13612	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
13613		uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
13614		zend_jit_addr var_addr = prop_addr;
13615
13616		if (use_prop_guard) {
13617			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
13618			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13619			if (!exit_addr) {
13620				return 0;
13621			}
13622
13623			|	IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr
13624			var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
13625		}
13626
13627		if (var_info & MAY_BE_REF) {
13628			may_throw = 1;
13629			var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13630			if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13631				|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13632			}
13633			|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2
13634			|	GET_ZVAL_PTR FCARG1a, var_addr
13635			|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
13636			|	jnz >1
13637			|	lea FCARG1a, aword [FCARG1a + offsetof(zend_reference, val)]
13638			|.cold_code
13639			|1:
13640			if (opline) {
13641				|	SET_EX_OPLINE opline, r0
13642			}
13643			if (opline->result_type == IS_UNUSED) {
13644				|	xor FCARG2a, FCARG2a
13645			} else {
13646				|	LOAD_ZVAL_ADDR FCARG2a, res_addr
13647			}
13648			switch (opline->opcode) {
13649				case ZEND_PRE_INC_OBJ:
13650					|	EXT_CALL zend_jit_pre_inc_typed_ref, r0
13651					break;
13652				case ZEND_PRE_DEC_OBJ:
13653					|	EXT_CALL zend_jit_pre_dec_typed_ref, r0
13654					break;
13655				case ZEND_POST_INC_OBJ:
13656					|	EXT_CALL zend_jit_post_inc_typed_ref, r0
13657					break;
13658				case ZEND_POST_DEC_OBJ:
13659					|	EXT_CALL zend_jit_post_dec_typed_ref, r0
13660					break;
13661				default:
13662					ZEND_UNREACHABLE();
13663			}
13664			|	jmp >9
13665			|.code
13666			|2:
13667		}
13668
13669		if (var_info & MAY_BE_LONG) {
13670			if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) {
13671				|	IF_NOT_ZVAL_TYPE var_addr, IS_LONG, >2
13672			}
13673			if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
13674				if (opline->result_type != IS_UNUSED) {
13675					|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_R1, ZREG_R2
13676				}
13677			}
13678			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13679				|	LONG_OP_WITH_32BIT_CONST add, var_addr, Z_L(1)
13680			} else {
13681				|	LONG_OP_WITH_32BIT_CONST sub, var_addr, Z_L(1)
13682			}
13683			|	jo	>3
13684			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) {
13685				if (opline->result_type != IS_UNUSED) {
13686					|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_R0, ZREG_R2
13687				}
13688			}
13689			|.cold_code
13690		}
13691		if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) {
13692			if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
13693				may_throw = 1;
13694			}
13695			if (var_info & MAY_BE_LONG) {
13696				|2:
13697			}
13698			if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
13699				var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13700				|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13701			}
13702			if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
13703				|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_ANY, ZREG_R0, ZREG_R2
13704				|	TRY_ADDREF MAY_BE_ANY, ah, r2
13705			}
13706			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13707				if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13708					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
13709					|	EXT_CALL zend_jit_pre_inc, r0
13710				} else {
13711					|	EXT_CALL increment_function, r0
13712				}
13713			} else {
13714				if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13715					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
13716					|	EXT_CALL zend_jit_pre_dec, r0
13717				} else {
13718					|	EXT_CALL decrement_function, r0
13719				}
13720			}
13721			if (var_info & MAY_BE_LONG) {
13722				|	jmp >4
13723			}
13724		}
13725		if (var_info & MAY_BE_LONG) {
13726			|3:
13727			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13728				|.if X64
13729					|	mov64 rax, 0x43e0000000000000
13730					|	SET_ZVAL_LVAL var_addr, rax
13731					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13732					if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13733						|	SET_ZVAL_LVAL res_addr, rax
13734						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13735					}
13736				|.else
13737					|	SET_ZVAL_LVAL var_addr, 0
13738					|	SET_ZVAL_W2 var_addr, 0x41e00000
13739					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13740					if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13741						|	SET_ZVAL_LVAL res_addr, 0
13742						|	SET_ZVAL_W2 res_addr, 0x41e00000
13743						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13744					}
13745				|.endif
13746			} else {
13747				|.if X64
13748					|	mov64 rax, 0xc3e0000000000000
13749					|	SET_ZVAL_LVAL var_addr, rax
13750					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13751					if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13752						|	SET_ZVAL_LVAL res_addr, rax
13753						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13754					}
13755				|.else
13756					|	SET_ZVAL_LVAL var_addr, 0x00200000
13757					|	SET_ZVAL_W2 var_addr, 0xc1e00000
13758					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13759					if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13760						|	SET_ZVAL_LVAL res_addr, 0x00200000
13761						|	SET_ZVAL_W2 res_addr, 0xc1e00000
13762						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13763					}
13764				|.endif
13765			}
13766			if (opline->result_type != IS_UNUSED
13767			 && (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ)
13768			 && prop_info
13769			 && !ZEND_TYPE_IS_SET(prop_info->type)
13770			 && (res_info & MAY_BE_GUARD)
13771			 && (res_info & MAY_BE_LONG)) {
13772				zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
13773				uint32_t old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
13774				int32_t exit_point;
13775				const void *exit_addr;
13776
13777				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
13778				exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
13779				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13780				if (!exit_addr) {
13781					return 0;
13782				}
13783				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
13784				ssa->var_info[ssa_op->result_def].type = res_info & ~MAY_BE_GUARD;
13785				|	jmp &exit_addr
13786				|.code
13787			} else {
13788				|	jmp >4
13789				|.code
13790				|4:
13791			}
13792		}
13793	}
13794
13795	if (needs_slow_path) {
13796		may_throw = 1;
13797		|.cold_code
13798		|7:
13799		|	SET_EX_OPLINE opline, r0
13800		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
13801		|	LOAD_ADDR FCARG2a, name
13802		|.if X64
13803			|	mov CARG3, EX->run_time_cache
13804			|	add CARG3, opline->extended_value
13805			if (opline->result_type == IS_UNUSED) {
13806				|	xor CARG4, CARG4
13807			} else {
13808				|	LOAD_ZVAL_ADDR CARG4, res_addr
13809			}
13810		|.else
13811			|	sub r4, 8
13812			if (opline->result_type == IS_UNUSED) {
13813				|	push 0
13814			} else {
13815				|	PUSH_ZVAL_ADDR res_addr, r0
13816			}
13817			|	mov r0, EX->run_time_cache
13818			|	add r0, opline->extended_value
13819			|	push r0
13820		|.endif
13821
13822		switch (opline->opcode) {
13823			case ZEND_PRE_INC_OBJ:
13824				|	EXT_CALL zend_jit_pre_inc_obj_helper, r0
13825				break;
13826			case ZEND_PRE_DEC_OBJ:
13827				|	EXT_CALL zend_jit_pre_dec_obj_helper, r0
13828				break;
13829			case ZEND_POST_INC_OBJ:
13830				|	EXT_CALL zend_jit_post_inc_obj_helper, r0
13831				break;
13832			case ZEND_POST_DEC_OBJ:
13833				|	EXT_CALL zend_jit_post_dec_obj_helper, r0
13834				break;
13835			default:
13836				ZEND_UNREACHABLE();
13837		}
13838
13839		|.if not(X64)
13840			|	add r4, 8
13841		|.endif
13842
13843		|	jmp >9
13844		|.code
13845	}
13846
13847	|9:
13848	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
13849		if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
13850			may_throw = 1;
13851		}
13852		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
13853	}
13854
13855	if (may_throw) {
13856		if (!zend_jit_check_exception(Dst)) {
13857			return 0;
13858		}
13859	}
13860
13861	return 1;
13862}
13863
13864static int zend_jit_assign_obj_op(dasm_State          **Dst,
13865                                  const zend_op        *opline,
13866                                  const zend_op_array  *op_array,
13867                                  zend_ssa             *ssa,
13868                                  const zend_ssa_op    *ssa_op,
13869                                  uint32_t              op1_info,
13870                                  zend_jit_addr         op1_addr,
13871                                  uint32_t              val_info,
13872                                  zend_ssa_range       *val_range,
13873                                  bool                  op1_indirect,
13874                                  zend_class_entry     *ce,
13875                                  bool                  ce_is_instanceof,
13876                                  bool                  on_this,
13877                                  bool                  delayed_fetch_this,
13878                                  zend_class_entry     *trace_ce,
13879                                  uint8_t               prop_type)
13880{
13881	zval *member;
13882	zend_string *name;
13883	zend_property_info *prop_info;
13884	zend_jit_addr val_addr = OP1_DATA_ADDR();
13885	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
13886	zend_jit_addr prop_addr;
13887	bool needs_slow_path = 0;
13888	bool use_prop_guard = 0;
13889	bool may_throw = 0;
13890	binary_op_type binary_op = get_binary_op(opline->extended_value);
13891
13892	ZEND_ASSERT(opline->op2_type == IS_CONST);
13893	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
13894	ZEND_ASSERT(opline->result_type == IS_UNUSED);
13895
13896	member = RT_CONSTANT(opline, opline->op2);
13897	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
13898	name = Z_STR_P(member);
13899	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
13900
13901	if (on_this) {
13902		|	GET_ZVAL_PTR FCARG1a, this_addr
13903	} else {
13904		if (opline->op1_type == IS_VAR
13905		 && (op1_info & MAY_BE_INDIRECT)
13906		 && Z_REG(op1_addr) == ZREG_FP) {
13907			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13908			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
13909			|	GET_Z_PTR FCARG1a, FCARG1a
13910			|1:
13911			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13912		}
13913		if (op1_info & MAY_BE_REF) {
13914			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13915				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13916			}
13917			|	ZVAL_DEREF FCARG1a, op1_info
13918			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13919		}
13920		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
13921			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13922				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13923				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13924
13925				if (!exit_addr) {
13926					return 0;
13927				}
13928				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
13929			} else {
13930				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
13931				|.cold_code
13932				|1:
13933				|	SET_EX_OPLINE opline, r0
13934				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13935					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13936				}
13937				|	LOAD_ADDR FCARG2a, ZSTR_VAL(name)
13938				if (op1_info & MAY_BE_UNDEF) {
13939					|	EXT_CALL zend_jit_invalid_property_assign_op, r0
13940				} else {
13941					|	EXT_CALL zend_jit_invalid_property_assign, r0
13942				}
13943				may_throw = 1;
13944				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
13945				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13946					may_throw = 1;
13947					|	jmp >8
13948				} else {
13949					|	jmp >9
13950				}
13951				|.code
13952			}
13953		}
13954		|	GET_ZVAL_PTR FCARG1a, op1_addr
13955	}
13956
13957	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
13958		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
13959		if (prop_info) {
13960			ce = trace_ce;
13961			ce_is_instanceof = 0;
13962			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
13963				if (on_this && JIT_G(current_frame)
13964				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
13965					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
13966				} else if (zend_jit_class_guard(Dst, opline, ce)) {
13967					if (on_this && JIT_G(current_frame)) {
13968						JIT_G(current_frame)->ce = ce;
13969						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
13970					}
13971				} else {
13972					return 0;
13973				}
13974				if (ssa->var_info && ssa_op->op1_use >= 0) {
13975					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
13976					ssa->var_info[ssa_op->op1_use].ce = ce;
13977					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
13978				}
13979				if (ssa->var_info && ssa_op->op1_def >= 0) {
13980					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
13981					ssa->var_info[ssa_op->op1_def].ce = ce;
13982					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
13983				}
13984			}
13985		}
13986	}
13987
13988	use_prop_guard = (prop_type != IS_UNKNOWN
13989		&& prop_type != IS_UNDEF
13990		&& prop_type != IS_REFERENCE
13991		&& (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT);
13992
13993	if (!prop_info) {
13994		needs_slow_path = 1;
13995
13996		|	mov r0, EX->run_time_cache
13997		|	mov r2, aword [r0 + (opline+1)->extended_value]
13998		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
13999		|	jne >7
14000		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
14001			|	cmp aword [r0 + (opline+1)->extended_value + sizeof(void*) * 2], 0
14002			|	jnz >7
14003		}
14004		|	mov r0, aword [r0 + (opline+1)->extended_value + sizeof(void*)]
14005		|	test r0, r0
14006		|	jl >7
14007		if (!use_prop_guard) {
14008			|	IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >7
14009		}
14010		|	add FCARG1a, r0
14011		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14012	} else {
14013		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
14014		if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) {
14015			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14016				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14017				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14018
14019				if (!exit_addr) {
14020					return 0;
14021				}
14022				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr
14023			} else {
14024				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >7
14025				needs_slow_path = 1;
14026			}
14027		}
14028		if (ZEND_TYPE_IS_SET(prop_info->type)) {
14029			uint32_t info = val_info;
14030
14031			may_throw = 1;
14032
14033			if (opline) {
14034				|	SET_EX_OPLINE opline, r0
14035			}
14036
14037			|	IF_ZVAL_TYPE prop_addr, IS_REFERENCE, >1
14038			|.cold_code
14039			|1:
14040			|	GET_ZVAL_PTR FCARG1a, prop_addr
14041			if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
14042				|	LOAD_ZVAL_ADDR FCARG2a, val_addr
14043			}
14044			|.if X64
14045				|	LOAD_ADDR CARG3, binary_op
14046			|.else
14047				|	sub r4, 12
14048				|	PUSH_ADDR binary_op, r0
14049			|.endif
14050			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
14051			 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14052				|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
14053			} else {
14054				|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
14055			}
14056			|.if not(X64)
14057				|	add r4, 12
14058			|.endif
14059			|	jmp >9
14060			|.code
14061
14062			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
14063
14064			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
14065				|	LOAD_ADDR FCARG2a, prop_info
14066			} else {
14067				int prop_info_offset =
14068					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
14069
14070				|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
14071				|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
14072				|	mov FCARG2a, aword[r0 + prop_info_offset]
14073			}
14074			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
14075			|.if X64
14076				|	LOAD_ZVAL_ADDR CARG3, val_addr
14077				|	LOAD_ADDR CARG4, binary_op
14078			|.else
14079				|	sub r4, 8
14080				|	PUSH_ADDR binary_op, r0
14081				|	PUSH_ZVAL_ADDR val_addr, r0
14082			|.endif
14083
14084			|	EXT_CALL zend_jit_assign_op_to_typed_prop, r0
14085
14086			|.if not(X64)
14087				|	add r4, 8
14088			|.endif
14089
14090			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14091				info |= MAY_BE_RC1|MAY_BE_RCN;
14092			}
14093
14094			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL
14095		}
14096	}
14097
14098	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
14099		zend_jit_addr var_addr = prop_addr;
14100		uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
14101		uint32_t var_def_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
14102
14103		if (use_prop_guard) {
14104			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
14105			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14106			if (!exit_addr) {
14107				return 0;
14108			}
14109
14110			|	IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr
14111			var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
14112		}
14113
14114		if (var_info & MAY_BE_REF) {
14115			may_throw = 1;
14116			var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
14117			|	LOAD_ZVAL_ADDR r0, prop_addr
14118			|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2
14119			|	GET_ZVAL_PTR FCARG1a, var_addr
14120			|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
14121			|	jnz >1
14122			|	lea r0, aword [FCARG1a + offsetof(zend_reference, val)]
14123			|.cold_code
14124			|1:
14125			if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
14126				|	LOAD_ZVAL_ADDR FCARG2a, val_addr
14127			}
14128			if (opline) {
14129				|	SET_EX_OPLINE opline, r0
14130			}
14131			|.if X64
14132				|	LOAD_ADDR CARG3, binary_op
14133			|.else
14134				|	sub r4, 12
14135				|	PUSH_ADDR binary_op, r0
14136			|.endif
14137			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
14138			 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14139				|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
14140			} else {
14141				|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
14142			}
14143			|.if not(X64)
14144				|	add r4, 12
14145			|.endif
14146			|	jmp >9
14147			|.code
14148			|2:
14149			var_info &= ~MAY_BE_REF;
14150		}
14151
14152		switch (opline->extended_value) {
14153			case ZEND_ADD:
14154			case ZEND_SUB:
14155			case ZEND_MUL:
14156				if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14157				    (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14158					if (opline->extended_value != ZEND_ADD ||
14159					    (var_info & MAY_BE_ANY) != MAY_BE_ARRAY ||
14160					    (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) {
14161						may_throw = 1;
14162					}
14163				}
14164				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,
14165						1 /* may overflow */, 0)) {
14166					return 0;
14167				}
14168				break;
14169			case ZEND_BW_OR:
14170			case ZEND_BW_AND:
14171			case ZEND_BW_XOR:
14172				may_throw = 1;
14173				if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14174				    (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14175					if ((var_info & MAY_BE_ANY) != MAY_BE_STRING ||
14176					    (val_info & MAY_BE_ANY) != MAY_BE_STRING) {
14177						may_throw = 1;
14178					}
14179				}
14180				goto long_math;
14181			case ZEND_SL:
14182			case ZEND_SR:
14183				if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14184				    (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14185					may_throw = 1;
14186				}
14187				if ((opline+1)->op1_type != IS_CONST ||
14188				    Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG ||
14189				    Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) < 0) {
14190					may_throw = 1;
14191				}
14192				goto long_math;
14193			case ZEND_MOD:
14194				if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14195				    (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14196					if (opline->extended_value != ZEND_ADD ||
14197					    (var_info & MAY_BE_ANY) != MAY_BE_ARRAY ||
14198					    (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) {
14199						may_throw = 1;
14200					}
14201				}
14202				if ((opline+1)->op1_type != IS_CONST ||
14203				    Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG ||
14204				    Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) == 0) {
14205					may_throw = 1;
14206				}
14207long_math:
14208				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
14209						IS_CV, opline->op1, var_addr, var_info, NULL,
14210						(opline+1)->op1_type, (opline+1)->op1, val_addr, val_info,
14211						val_range,
14212						0, var_addr, var_def_info, var_info, /* may throw */ 1)) {
14213					return 0;
14214				}
14215				break;
14216			case ZEND_CONCAT:
14217				may_throw = 1;
14218				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,
14219						0)) {
14220					return 0;
14221				}
14222				break;
14223			default:
14224				ZEND_UNREACHABLE();
14225		}
14226	}
14227
14228	if (needs_slow_path) {
14229		may_throw = 1;
14230		|.cold_code
14231		|7:
14232		|	SET_EX_OPLINE opline, r0
14233		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
14234		|	LOAD_ADDR FCARG2a, name
14235		|.if X64
14236			|	LOAD_ZVAL_ADDR CARG3, val_addr
14237			|	mov CARG4, EX->run_time_cache
14238			|	add CARG4, (opline+1)->extended_value
14239			|.if X64WIN
14240			|	LOAD_ADDR r0, binary_op
14241			|	mov aword A5, r0
14242			|.else
14243			|	LOAD_ADDR CARG5, binary_op
14244			|.endif
14245		|.else
14246			|	sub r4, 4
14247			|	PUSH_ADDR binary_op, r0
14248			|	mov r0, EX->run_time_cache
14249			|	add r0, (opline+1)->extended_value
14250			|	push r0
14251			|	PUSH_ZVAL_ADDR val_addr, r0
14252		|.endif
14253
14254		|	EXT_CALL zend_jit_assign_obj_op_helper, r0
14255
14256		|.if not(X64)
14257			|	add r4, 4
14258		|.endif
14259
14260		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14261			val_info |= MAY_BE_RC1|MAY_BE_RCN;
14262		}
14263
14264		|8:
14265		|	// FREE_OP_DATA();
14266		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline
14267		|	jmp >9
14268		|.code
14269	}
14270
14271	|9:
14272	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
14273		if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
14274			may_throw = 1;
14275		}
14276		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
14277	}
14278
14279	if (may_throw) {
14280		if (!zend_jit_check_exception(Dst)) {
14281			return 0;
14282		}
14283	}
14284
14285	return 1;
14286}
14287
14288static int zend_jit_assign_obj(dasm_State          **Dst,
14289                               const zend_op        *opline,
14290                               const zend_op_array  *op_array,
14291                               zend_ssa             *ssa,
14292                               const zend_ssa_op    *ssa_op,
14293                               uint32_t              op1_info,
14294                               zend_jit_addr         op1_addr,
14295                               uint32_t              val_info,
14296                               bool                  op1_indirect,
14297                               zend_class_entry     *ce,
14298                               bool                  ce_is_instanceof,
14299                               bool                  on_this,
14300                               bool                  delayed_fetch_this,
14301                               zend_class_entry     *trace_ce,
14302                               uint8_t               prop_type,
14303                               int                   may_throw)
14304{
14305	zval *member;
14306	zend_string *name;
14307	zend_property_info *prop_info;
14308	zend_jit_addr val_addr = OP1_DATA_ADDR();
14309	zend_jit_addr res_addr = 0;
14310	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
14311	zend_jit_addr prop_addr;
14312	bool needs_slow_path = 0;
14313	bool needs_val_dtor = 0;
14314
14315	if (RETURN_VALUE_USED(opline)) {
14316		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
14317	}
14318
14319	ZEND_ASSERT(opline->op2_type == IS_CONST);
14320	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
14321
14322	member = RT_CONSTANT(opline, opline->op2);
14323	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
14324	name = Z_STR_P(member);
14325	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
14326
14327	if (on_this) {
14328		|	GET_ZVAL_PTR FCARG1a, this_addr
14329	} else {
14330		if (opline->op1_type == IS_VAR
14331		 && (op1_info & MAY_BE_INDIRECT)
14332		 && Z_REG(op1_addr) == ZREG_FP) {
14333			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14334			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
14335			|	GET_Z_PTR FCARG1a, FCARG1a
14336			|1:
14337			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14338		}
14339		if (op1_info & MAY_BE_REF) {
14340			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
14341				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14342			}
14343			|	ZVAL_DEREF FCARG1a, op1_info
14344			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14345		}
14346		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
14347			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14348				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14349				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14350
14351				if (!exit_addr) {
14352					return 0;
14353				}
14354				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
14355			} else {
14356				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
14357				|.cold_code
14358				|1:
14359				|	SET_EX_OPLINE opline, r0
14360				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
14361					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14362				}
14363				|	LOAD_ADDR FCARG2a, ZSTR_VAL(name)
14364				|	EXT_CALL zend_jit_invalid_property_assign, r0
14365				if (RETURN_VALUE_USED(opline)) {
14366					|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
14367				}
14368				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
14369				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14370				 	needs_val_dtor = 1;
14371					|	jmp >7
14372				} else {
14373					|	jmp >9
14374				}
14375				|.code
14376			}
14377		}
14378		|	GET_ZVAL_PTR FCARG1a, op1_addr
14379	}
14380
14381	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
14382		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
14383		if (prop_info) {
14384			ce = trace_ce;
14385			ce_is_instanceof = 0;
14386			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
14387				if (on_this && JIT_G(current_frame)
14388				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
14389					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
14390				} else if (zend_jit_class_guard(Dst, opline, ce)) {
14391					if (on_this && JIT_G(current_frame)) {
14392						JIT_G(current_frame)->ce = ce;
14393						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
14394					}
14395				} else {
14396					return 0;
14397				}
14398				if (ssa->var_info && ssa_op->op1_use >= 0) {
14399					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
14400					ssa->var_info[ssa_op->op1_use].ce = ce;
14401					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
14402				}
14403				if (ssa->var_info && ssa_op->op1_def >= 0) {
14404					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
14405					ssa->var_info[ssa_op->op1_def].ce = ce;
14406					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
14407				}
14408			}
14409		}
14410	}
14411
14412	if (!prop_info) {
14413		needs_slow_path = 1;
14414
14415		|	mov r0, EX->run_time_cache
14416		|	mov r2, aword [r0 + opline->extended_value]
14417		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
14418		|	jne >5
14419		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
14420			|	mov FCARG2a, aword [r0 + opline->extended_value + sizeof(void*) * 2]
14421		}
14422		|	mov r0, aword [r0 + opline->extended_value + sizeof(void*)]
14423		|	test r0, r0
14424		|	jl >5
14425		|	IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >5
14426		|	add FCARG1a, r0
14427		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14428		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
14429			|	test FCARG2a, FCARG2a
14430			|	jnz >1
14431			|.cold_code
14432			|1:
14433			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
14434			|	SET_EX_OPLINE opline, r0
14435			|.if X64
14436				|	LOAD_ZVAL_ADDR CARG3, val_addr
14437				if (RETURN_VALUE_USED(opline)) {
14438					|	LOAD_ZVAL_ADDR CARG4, res_addr
14439				} else {
14440					|	xor CARG4, CARG4
14441				}
14442			|.else
14443				|	sub r4, 8
14444				if (RETURN_VALUE_USED(opline)) {
14445					|	PUSH_ZVAL_ADDR res_addr, r0
14446				} else {
14447					|	push 0
14448				}
14449				|	PUSH_ZVAL_ADDR val_addr, r0
14450			|.endif
14451
14452			|	EXT_CALL zend_jit_assign_to_typed_prop, r0
14453
14454			|.if not(X64)
14455				|	add r4, 8
14456			|.endif
14457
14458			if ((opline+1)->op1_type == IS_CONST) {
14459				|	// TODO: ???
14460				|	// if (Z_TYPE_P(value) == orig_type) {
14461				|	// CACHE_PTR_EX(cache_slot + 2, NULL);
14462			}
14463
14464			if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
14465			 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14466				|	jmp >7
14467			} else {
14468				|	jmp >9
14469			}
14470			|.code
14471		}
14472	} else {
14473		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
14474		if (!ce || ce_is_instanceof || !(ce->ce_flags & ZEND_ACC_IMMUTABLE) || ce->__get || ce->__set || (prop_info->flags & ZEND_ACC_READONLY)) {
14475			// Undefined property with magic __get()/__set()
14476			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14477				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14478				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14479
14480				if (!exit_addr) {
14481					return 0;
14482				}
14483				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr
14484			} else {
14485				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >5
14486				needs_slow_path = 1;
14487			}
14488		}
14489		if (ZEND_TYPE_IS_SET(prop_info->type)) {
14490			uint32_t info = val_info;
14491
14492			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
14493			|	SET_EX_OPLINE opline, r0
14494			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
14495				|	LOAD_ADDR FCARG2a, prop_info
14496			} else {
14497				int prop_info_offset =
14498					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
14499
14500				|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
14501				|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
14502				|	mov FCARG2a, aword[r0 + prop_info_offset]
14503			}
14504			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
14505			|.if X64
14506				|	LOAD_ZVAL_ADDR CARG3, val_addr
14507				if (RETURN_VALUE_USED(opline)) {
14508					|	LOAD_ZVAL_ADDR CARG4, res_addr
14509				} else {
14510					|	xor CARG4, CARG4
14511				}
14512			|.else
14513				|	sub r4, 8
14514				if (RETURN_VALUE_USED(opline)) {
14515					|	PUSH_ZVAL_ADDR res_addr, r0
14516				} else {
14517					|	push 0
14518				}
14519				|	PUSH_ZVAL_ADDR val_addr, r0
14520			|.endif
14521
14522			|	EXT_CALL zend_jit_assign_to_typed_prop, r0
14523
14524			|.if not(X64)
14525				|	add r4, 8
14526			|.endif
14527
14528			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14529				info |= MAY_BE_RC1|MAY_BE_RCN;
14530			}
14531
14532			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL
14533		}
14534	}
14535
14536	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
14537		// value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES());
14538		if (opline->result_type == IS_UNUSED) {
14539			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)) {
14540				return 0;
14541			}
14542		} else {
14543			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)) {
14544				return 0;
14545			}
14546		}
14547	}
14548
14549	if (needs_slow_path) {
14550		|.cold_code
14551		|5:
14552		|	SET_EX_OPLINE opline, r0
14553		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
14554		|	LOAD_ADDR FCARG2a, name
14555		|.if X64
14556			|	LOAD_ZVAL_ADDR CARG3, val_addr
14557			|	mov CARG4, EX->run_time_cache
14558			|	add CARG4, opline->extended_value
14559			if (RETURN_VALUE_USED(opline)) {
14560				|.if X64WIN
14561				|	LOAD_ZVAL_ADDR r0, res_addr
14562				|	mov aword A5, r0
14563				|.else
14564				|	LOAD_ZVAL_ADDR CARG5, res_addr
14565				|.endif
14566			} else {
14567				|.if X64WIN
14568				|	mov aword A5, 0
14569				|.else
14570				|	xor CARG5, CARG5
14571				|.endif
14572			}
14573		|.else
14574			|	sub r4, 4
14575			if (RETURN_VALUE_USED(opline)) {
14576				|	PUSH_ZVAL_ADDR res_addr, r0
14577			} else {
14578				|	push 0
14579			}
14580			|	mov r0, EX->run_time_cache
14581			|	add r0, opline->extended_value
14582			|	push r0
14583			|	PUSH_ZVAL_ADDR val_addr, r0
14584		|.endif
14585
14586		|	EXT_CALL zend_jit_assign_obj_helper, r0
14587
14588		|.if not(X64)
14589			|	add r4, 4
14590		|.endif
14591
14592		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14593			val_info |= MAY_BE_RC1|MAY_BE_RCN;
14594		}
14595
14596		|7:
14597		|	// FREE_OP_DATA();
14598		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline
14599		|	jmp >9
14600		|.code
14601	} else if (needs_val_dtor) {
14602		|.cold_code
14603		|7:
14604		|	// FREE_OP_DATA();
14605		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline
14606		|	jmp >9
14607		|.code
14608	}
14609
14610	|9:
14611	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
14612		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
14613	}
14614
14615	if (may_throw) {
14616		if (!zend_jit_check_exception(Dst)) {
14617			return 0;
14618		}
14619	}
14620
14621	return 1;
14622}
14623
14624static int zend_jit_free(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, int may_throw)
14625{
14626	zend_jit_addr op1_addr = OP1_ADDR();
14627
14628	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
14629		if (may_throw) {
14630			|	SET_EX_OPLINE opline, r0
14631		}
14632		if (opline->opcode == ZEND_FE_FREE && (op1_info & (MAY_BE_OBJECT|MAY_BE_REF))) {
14633			if (op1_info & MAY_BE_ARRAY) {
14634				|	IF_ZVAL_TYPE op1_addr, IS_ARRAY, >7
14635			}
14636			|	mov FCARG1d, dword [FP + opline->op1.var + offsetof(zval, u2.fe_iter_idx)]
14637			|	cmp FCARG1d, -1
14638			|	je >7
14639			|	EXT_CALL zend_hash_iterator_del, r0
14640			|7:
14641		}
14642		|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline
14643		if (may_throw) {
14644			if (!zend_jit_check_exception(Dst)) {
14645				return 0;
14646			}
14647		}
14648	}
14649
14650	return 1;
14651}
14652
14653static int zend_jit_echo(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
14654{
14655	if (opline->op1_type == IS_CONST) {
14656		zval *zv;
14657		size_t len;
14658
14659		zv = RT_CONSTANT(opline, opline->op1);
14660		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
14661		len = Z_STRLEN_P(zv);
14662
14663		if (len > 0) {
14664			const char *str = Z_STRVAL_P(zv);
14665
14666			|	SET_EX_OPLINE opline, r0
14667			|.if X64
14668				|	LOAD_ADDR CARG1, str
14669				|	LOAD_ADDR CARG2, len
14670				|	EXT_CALL zend_write, r0
14671			|.else
14672				|	mov aword A2, len
14673				|	mov aword A1, str
14674				|	EXT_CALL zend_write, r0
14675			|.endif
14676			if (!zend_jit_check_exception(Dst)) {
14677				return 0;
14678			}
14679		}
14680	} else {
14681		zend_jit_addr op1_addr = OP1_ADDR();
14682
14683		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
14684
14685		|	SET_EX_OPLINE opline, r0
14686		|	GET_ZVAL_PTR r0, op1_addr
14687		|.if X64
14688		|	lea CARG1, aword [r0 + offsetof(zend_string, val)]
14689		|	mov CARG2, aword [r0 + offsetof(zend_string, len)]
14690		|	EXT_CALL zend_write, r0
14691		|.else
14692		|	add r0, offsetof(zend_string, val)
14693		|	mov aword A1, r0
14694		|	mov r0, aword [r0 + (offsetof(zend_string, len)-offsetof(zend_string, val))]
14695		|	mov aword A2, r0
14696		|	EXT_CALL zend_write, r0
14697		|.endif
14698		if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
14699			|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline
14700		}
14701		if (!zend_jit_check_exception(Dst)) {
14702			return 0;
14703		}
14704	}
14705	return 1;
14706}
14707
14708static 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)
14709{
14710	if (opline->op1_type == IS_CONST) {
14711		zval *zv;
14712		size_t len;
14713
14714		zv = RT_CONSTANT(opline, opline->op1);
14715		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
14716		len = Z_STRLEN_P(zv);
14717
14718		|	SET_ZVAL_LVAL res_addr, len
14719		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
14720			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14721		} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14722			return 0;
14723		}
14724	} else {
14725		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
14726
14727		if (Z_MODE(res_addr) == IS_REG) {
14728			|	GET_ZVAL_PTR Ra(Z_REG(res_addr)), op1_addr
14729			|	mov Ra(Z_REG(res_addr)), aword [Ra(Z_REG(res_addr))+offsetof(zend_string, len)]
14730			if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14731				return 0;
14732			}
14733		} else {
14734			|	GET_ZVAL_PTR r0, op1_addr
14735			|	mov r0, aword [r0 + offsetof(zend_string, len)]
14736			|	SET_ZVAL_LVAL res_addr, r0
14737			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14738		}
14739		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
14740	}
14741	return 1;
14742}
14743
14744static int zend_jit_count(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr res_addr, int may_throw)
14745{
14746	if (opline->op1_type == IS_CONST) {
14747		zval *zv;
14748		zend_long count;
14749
14750		zv = RT_CONSTANT(opline, opline->op1);
14751		ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY);
14752		count = zend_hash_num_elements(Z_ARRVAL_P(zv));
14753
14754		|	SET_ZVAL_LVAL res_addr, count
14755		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
14756			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14757		} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14758			return 0;
14759		}
14760	} else {
14761		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY);
14762		// Note: See the implementation of ZEND_COUNT in Zend/zend_vm_def.h - arrays do not contain IS_UNDEF starting in php 8.1+.
14763
14764		if (Z_MODE(res_addr) == IS_REG) {
14765			|	GET_ZVAL_PTR Ra(Z_REG(res_addr)), op1_addr
14766			// Sign-extend the 32-bit value to a potentially 64-bit zend_long
14767			|	mov Rd(Z_REG(res_addr)), dword [Ra(Z_REG(res_addr))+offsetof(HashTable, nNumOfElements)]
14768			if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14769				return 0;
14770			}
14771		} else {
14772			|	GET_ZVAL_PTR r0, op1_addr
14773			// Sign-extend the 32-bit value to a potentially 64-bit zend_long
14774			|	mov eax, dword [r0 + offsetof(HashTable, nNumOfElements)]
14775			|	SET_ZVAL_LVAL res_addr, r0
14776			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14777		}
14778		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
14779	}
14780
14781	if (may_throw) {
14782		return zend_jit_check_exception(Dst);
14783	}
14784	return 1;
14785}
14786
14787static int zend_jit_load_this(dasm_State **Dst, uint32_t var)
14788{
14789	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
14790
14791	|	mov FCARG1a, aword EX->This.value.ptr
14792	|	SET_ZVAL_PTR var_addr, FCARG1a
14793	|	SET_ZVAL_TYPE_INFO var_addr, IS_OBJECT_EX
14794	|	GC_ADDREF FCARG1a
14795
14796	return 1;
14797}
14798
14799static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool check_only)
14800{
14801	if (!op_array->scope ||
14802			(op_array->fn_flags & ZEND_ACC_STATIC) ||
14803			((op_array->fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_IMMUTABLE)) == ZEND_ACC_CLOSURE)) {
14804		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14805			if (!JIT_G(current_frame) ||
14806			    !TRACE_FRAME_IS_THIS_CHECKED(JIT_G(current_frame))) {
14807
14808				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14809				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14810
14811				if (!exit_addr) {
14812					return 0;
14813				}
14814
14815				|	cmp byte EX->This.u1.v.type, IS_OBJECT
14816				|	jne &exit_addr
14817
14818				if (JIT_G(current_frame)) {
14819					TRACE_FRAME_SET_THIS_CHECKED(JIT_G(current_frame));
14820				}
14821			}
14822		} else {
14823
14824			|	cmp byte EX->This.u1.v.type, IS_OBJECT
14825			|	jne >1
14826			|.cold_code
14827			|1:
14828			|	SET_EX_OPLINE opline, r0
14829			|	jmp ->invalid_this
14830			|.code
14831		}
14832	}
14833
14834	if (!check_only) {
14835		if (!zend_jit_load_this(Dst, opline->result.var)) {
14836			return 0;
14837		}
14838	}
14839
14840	return 1;
14841}
14842
14843static 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)
14844{
14845	uint32_t count;
14846	Bucket *p;
14847	const zend_op *target;
14848	int b;
14849	int32_t exit_point;
14850	const void *exit_addr;
14851
14852	|	test r0, r0
14853	if (default_label) {
14854		|	jz &default_label
14855	} else if (next_opline) {
14856		|	jz >3
14857	} else {
14858		|	jz =>default_b
14859	}
14860	|	LOAD_ADDR FCARG1a, jumptable
14861	|	sub r0, aword [FCARG1a + offsetof(HashTable, arData)]
14862	if (HT_IS_PACKED(jumptable)) {
14863		|	mov FCARG1a, (sizeof(zval) / sizeof(void*))
14864	}	else {
14865		|	mov FCARG1a, (sizeof(Bucket) / sizeof(void*))
14866	}
14867	|.if X64
14868	|	cqo
14869	|.else
14870	|	cdq
14871	|.endif
14872	|	idiv FCARG1a
14873	|.if X64
14874	if (!IS_32BIT(dasm_end)) {
14875		|	lea FCARG1a, aword [>4]
14876		|	jmp aword [FCARG1a + r0]
14877	} else {
14878		|	jmp aword [r0 + >4]
14879	}
14880	|.else
14881	|	jmp aword [r0 + >4]
14882	|.endif
14883	|.jmp_table
14884	|.align aword
14885	|4:
14886	if (trace_info) {
14887		trace_info->jmp_table_size += zend_hash_num_elements(jumptable);
14888	}
14889
14890	count = jumptable->nNumUsed;
14891	p = jumptable->arData;
14892	do {
14893		if (Z_TYPE(p->val) == IS_UNDEF) {
14894			if (default_label) {
14895				|	.aword &default_label
14896			} else if (next_opline) {
14897				|	.aword >3
14898			} else {
14899				|	.aword =>default_b
14900			}
14901		} else {
14902			target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val));
14903			if (!next_opline) {
14904				b = ssa->cfg.map[target - op_array->opcodes];
14905				|	.aword =>b
14906			} else if (next_opline == target) {
14907				|	.aword >3
14908			} else {
14909				exit_point = zend_jit_trace_get_exit_point(target, 0);
14910				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14911				if (!exit_addr) {
14912					return 0;
14913				}
14914				|	.aword &exit_addr
14915			}
14916		}
14917		if (HT_IS_PACKED(jumptable)) {
14918			p = (Bucket*)(((zval*)p)+1);
14919		} else {
14920			p++;
14921		}
14922		count--;
14923	} while (count);
14924	|.code
14925
14926	return 1;
14927}
14928
14929static 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)
14930{
14931	HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
14932	const zend_op *next_opline = NULL;
14933
14934	if (trace) {
14935		ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
14936		ZEND_ASSERT(trace->opline != NULL);
14937		next_opline = trace->opline;
14938	}
14939
14940	if (opline->op1_type == IS_CONST) {
14941		zval *zv = RT_CONSTANT(opline, opline->op1);
14942		zval *jump_zv = NULL;
14943		int b;
14944
14945		if (opline->opcode == ZEND_SWITCH_LONG) {
14946			if (Z_TYPE_P(zv) == IS_LONG) {
14947				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
14948			}
14949		} else if (opline->opcode == ZEND_SWITCH_STRING) {
14950			if (Z_TYPE_P(zv) == IS_STRING) {
14951				jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv));
14952			}
14953		} else if (opline->opcode == ZEND_MATCH) {
14954			if (Z_TYPE_P(zv) == IS_LONG) {
14955				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
14956			} else if (Z_TYPE_P(zv) == IS_STRING) {
14957				jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv));
14958			}
14959		} else {
14960			ZEND_UNREACHABLE();
14961		}
14962		if (next_opline) {
14963			const zend_op *target;
14964
14965			if (jump_zv != NULL) {
14966				target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
14967			} else {
14968				target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
14969			}
14970			ZEND_ASSERT(target == next_opline);
14971		} else {
14972			if (jump_zv != NULL) {
14973				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes];
14974			} else {
14975				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
14976			}
14977			|	jmp =>b
14978		}
14979	} else {
14980		zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
14981		uint32_t op1_info = OP1_INFO();
14982		zend_jit_addr op1_addr = OP1_ADDR();
14983		const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
14984		const zend_op *target;
14985		int default_b = next_opline ? -1 : ssa->cfg.map[default_opline - op_array->opcodes];
14986		int b;
14987		int32_t exit_point;
14988		const void *fallback_label = NULL;
14989		const void *default_label = NULL;
14990		const void *exit_addr;
14991
14992		if (next_opline) {
14993			if (next_opline != opline + 1) {
14994				exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
14995				fallback_label = zend_jit_trace_get_exit_addr(exit_point);
14996				if (!fallback_label) {
14997					return 0;
14998				}
14999			}
15000			if (next_opline != default_opline) {
15001				exit_point = zend_jit_trace_get_exit_point(default_opline, 0);
15002				default_label = zend_jit_trace_get_exit_addr(exit_point);
15003				if (!default_label) {
15004					return 0;
15005				}
15006			}
15007		}
15008
15009		if (opline->opcode == ZEND_SWITCH_LONG) {
15010			if (op1_info & MAY_BE_LONG) {
15011				if (op1_info & MAY_BE_REF) {
15012					|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >1
15013					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr
15014					|.cold_code
15015					|1:
15016					|	// ZVAL_DEREF(op)
15017					if (fallback_label) {
15018						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label
15019					} else {
15020						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3
15021					}
15022					|	GET_ZVAL_PTR FCARG2a, op1_addr
15023					if (fallback_label) {
15024						|	IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_LONG, &fallback_label
15025					} else {
15026						|	IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_LONG, >3
15027					}
15028					|	mov FCARG2a, aword [FCARG2a + offsetof(zend_reference, val.value.lval)]
15029					|	jmp >2
15030					|.code
15031					|2:
15032				} else {
15033					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
15034						if (fallback_label) {
15035							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &fallback_label
15036						} else {
15037							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
15038						}
15039					}
15040					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr
15041				}
15042				if (HT_IS_PACKED(jumptable)) {
15043					uint32_t count = jumptable->nNumUsed;
15044					zval *zv = jumptable->arPacked;
15045
15046					|	cmp FCARG2a, jumptable->nNumUsed
15047					if (default_label) {
15048						|	jae &default_label
15049					} else if (next_opline) {
15050						|	jae >3
15051					} else {
15052						|	jae =>default_b
15053					}
15054					|.if X64
15055						if (!IS_32BIT(dasm_end)) {
15056							|	lea r0, aword [>4]
15057							|	jmp aword [r0 + FCARG2a * 8]
15058						} else {
15059							|	jmp aword [FCARG2a * 8 + >4]
15060						}
15061					|.else
15062					|	jmp aword [FCARG2a * 4 + >4]
15063					|.endif
15064					|.jmp_table
15065					|.align aword
15066					|4:
15067					if (trace_info) {
15068						trace_info->jmp_table_size += count;
15069					}
15070					do {
15071						if (Z_TYPE_P(zv) == IS_UNDEF) {
15072							if (default_label) {
15073								|	.aword &default_label
15074							} else if (next_opline) {
15075								|	.aword >3
15076							} else {
15077								|	.aword =>default_b
15078							}
15079						} else {
15080							target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(zv));
15081							if (!next_opline) {
15082								b = ssa->cfg.map[target - op_array->opcodes];
15083								|	.aword =>b
15084							} else if (next_opline == target) {
15085								|	.aword >3
15086							} else {
15087								exit_point = zend_jit_trace_get_exit_point(target, 0);
15088								exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15089								if (!exit_addr) {
15090									return 0;
15091								}
15092								|	.aword &exit_addr
15093							}
15094						}
15095						zv++;
15096						count--;
15097					} while (count);
15098					|.code
15099					|3:
15100				} else {
15101					|	LOAD_ADDR FCARG1a, jumptable
15102					|	EXT_CALL zend_hash_index_find, r0
15103					if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
15104						return 0;
15105					}
15106					|3:
15107				}
15108			}
15109		} else if (opline->opcode == ZEND_SWITCH_STRING) {
15110			if (op1_info & MAY_BE_STRING) {
15111				if (op1_info & MAY_BE_REF) {
15112					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >1
15113					|	GET_ZVAL_PTR FCARG2a, op1_addr
15114					|.cold_code
15115					|1:
15116					|	// ZVAL_DEREF(op)
15117					if (fallback_label) {
15118						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label
15119					} else {
15120						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3
15121					}
15122					|	GET_ZVAL_PTR FCARG2a, op1_addr
15123					if (fallback_label) {
15124						|	IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_STRING, &fallback_label
15125					} else {
15126						|	IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_STRING, >3
15127					}
15128					|	mov FCARG2a, aword [FCARG2a + offsetof(zend_reference, val.value.ptr)]
15129					|	jmp >2
15130					|.code
15131					|2:
15132				} else {
15133					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_STRING)) {
15134						if (fallback_label) {
15135							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &fallback_label
15136						} else {
15137							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3
15138						}
15139					}
15140					|	GET_ZVAL_PTR FCARG2a, op1_addr
15141				}
15142				|	LOAD_ADDR FCARG1a, jumptable
15143				|	EXT_CALL zend_hash_find, r0
15144				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
15145					return 0;
15146				}
15147				|3:
15148			}
15149		} else if (opline->opcode == ZEND_MATCH) {
15150			if (op1_info & (MAY_BE_LONG|MAY_BE_STRING)) {
15151				if (op1_info & MAY_BE_REF) {
15152					|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
15153					|	ZVAL_DEREF FCARG2a, op1_info
15154					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
15155				}
15156				|	LOAD_ADDR FCARG1a, jumptable
15157				if (op1_info & MAY_BE_LONG) {
15158					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
15159						if (op1_info & MAY_BE_STRING) {
15160							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >5
15161						} else if (op1_info & MAY_BE_UNDEF) {
15162							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
15163						} else if (default_label) {
15164							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &default_label
15165						} else if (next_opline) {
15166							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
15167						} else {
15168							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, =>default_b
15169						}
15170					}
15171					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr
15172					|	EXT_CALL zend_hash_index_find, r0
15173					if (op1_info & MAY_BE_STRING) {
15174						|	jmp >2
15175					}
15176				}
15177				if (op1_info & MAY_BE_STRING) {
15178					|5:
15179					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_STRING))) {
15180						if (op1_info & MAY_BE_UNDEF) {
15181							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6
15182						} else if (default_label) {
15183							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &default_label
15184						} else if (next_opline) {
15185							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3
15186						} else {
15187							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, =>default_b
15188						}
15189					}
15190					|	GET_ZVAL_PTR FCARG2a, op1_addr
15191					|	EXT_CALL zend_hash_find, r0
15192				}
15193				|2:
15194				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
15195					return 0;
15196				}
15197			}
15198			if (op1_info & MAY_BE_UNDEF) {
15199				|6:
15200				if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_STRING))) {
15201					if (default_label) {
15202						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, &default_label
15203					} else if (next_opline) {
15204						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >3
15205					} else {
15206						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, =>default_b
15207					}
15208				}
15209				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
15210				|	SET_EX_OPLINE opline, r0
15211				|	mov FCARG1d, opline->op1.var
15212				|	EXT_CALL zend_jit_undefined_op_helper, r0
15213				if (!zend_jit_check_exception_undef_result(Dst, opline)) {
15214					return 0;
15215				}
15216			}
15217			if (default_label) {
15218				|	jmp &default_label
15219			} else if (next_opline) {
15220				|	jmp >3
15221			} else {
15222				|	jmp =>default_b
15223			}
15224			|3:
15225		} else {
15226			ZEND_UNREACHABLE();
15227		}
15228	}
15229	return 1;
15230}
15231
15232static bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info)
15233{
15234	zend_arg_info *arg_info = &op_array->arg_info[-1];
15235	ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
15236	zend_jit_addr op1_addr = OP1_ADDR();
15237	bool needs_slow_check = 1;
15238	bool slow_check_in_cold = 1;
15239	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
15240
15241	if (type_mask == 0) {
15242		slow_check_in_cold = 0;
15243	} else {
15244		if (((op1_info & MAY_BE_ANY) & type_mask) == 0) {
15245			slow_check_in_cold = 0;
15246		} else if (((op1_info & MAY_BE_ANY) | type_mask) == type_mask) {
15247			needs_slow_check = 0;
15248		} else if (is_power_of_two(type_mask)) {
15249			uint32_t type_code = concrete_type(type_mask);
15250			|	IF_NOT_ZVAL_TYPE op1_addr, type_code, >6
15251		} else {
15252			|	mov edx, 1
15253			|	GET_ZVAL_TYPE cl, op1_addr
15254			|	shl edx, cl
15255			|	test edx, type_mask
15256			|	je >6
15257		}
15258	}
15259	if (needs_slow_check) {
15260		if (slow_check_in_cold) {
15261			|.cold_code
15262			|6:
15263		}
15264		|	SET_EX_OPLINE opline, r1
15265		if (op1_info & MAY_BE_UNDEF) {
15266			|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >7
15267			|	mov FCARG1a, opline->op1.var
15268			|	EXT_CALL zend_jit_undefined_op_helper, FCARG2a
15269			|	test r0, r0
15270			|	jz ->exception_handler
15271			|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
15272			|	jmp >8
15273		}
15274		|7:
15275		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
15276		|8:
15277		|	mov FCARG2a, EX->func
15278		|.if X64
15279			|	LOAD_ADDR CARG3, (ptrdiff_t)arg_info
15280			|	mov r0, EX->run_time_cache
15281			|	lea CARG4, aword [r0+opline->op2.num]
15282			|	EXT_CALL zend_jit_verify_return_slow, r0
15283		|.else
15284			|	sub r4, 8
15285			|	mov r0, EX->run_time_cache
15286			|	add r0, opline->op2.num
15287			|	push r0
15288			|	push (ptrdiff_t)arg_info
15289			|	EXT_CALL zend_jit_verify_return_slow, r0
15290			|	add r4, 8
15291		|.endif
15292		if (!zend_jit_check_exception(Dst)) {
15293			return 0;
15294		}
15295		if (slow_check_in_cold) {
15296			|	jmp >9
15297			|.code
15298		}
15299	}
15300	|9:
15301	return 1;
15302}
15303
15304static int zend_jit_isset_isempty_cv(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr,  uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
15305{
15306	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
15307
15308	// TODO: support for empty() ???
15309	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
15310
15311	if (op1_info & MAY_BE_REF) {
15312		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
15313			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
15314			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
15315		}
15316		|	ZVAL_DEREF FCARG1a, op1_info
15317		|1:
15318	}
15319
15320	if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) {
15321		if (exit_addr) {
15322			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPZ);
15323		} else if (smart_branch_opcode) {
15324			if (smart_branch_opcode == ZEND_JMPNZ) {
15325				|	jmp =>target_label
15326			}
15327		} else {
15328			|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
15329		}
15330	} else if (!(op1_info & (MAY_BE_ANY - MAY_BE_NULL))) {
15331		if (exit_addr) {
15332			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPNZ);
15333		} else if (smart_branch_opcode) {
15334			if (smart_branch_opcode != ZEND_JMPNZ) {
15335				|	jmp =>target_label
15336			}
15337		} else {
15338			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
15339		}
15340	} else {
15341		ZEND_ASSERT(Z_MODE(op1_addr) == IS_MEM_ZVAL);
15342		|	cmp byte [Ra(Z_REG(op1_addr))+Z_OFFSET(op1_addr)+offsetof(zval, u1.v.type)], IS_NULL
15343		if (exit_addr) {
15344			if (smart_branch_opcode == ZEND_JMPNZ) {
15345				|	jg &exit_addr
15346			} else {
15347				|	jle &exit_addr
15348			}
15349		} else if (smart_branch_opcode) {
15350			if (smart_branch_opcode == ZEND_JMPZ) {
15351				|	jle =>target_label
15352			} else if (smart_branch_opcode == ZEND_JMPNZ) {
15353				|	jg =>target_label
15354			} else {
15355				ZEND_UNREACHABLE();
15356			}
15357		} else {
15358			|	setg al
15359			|	movzx eax, al
15360			|	lea eax, [eax + IS_FALSE]
15361			|	SET_ZVAL_TYPE_INFO res_addr, eax
15362		}
15363	}
15364
15365	return 1;
15366}
15367
15368static int zend_jit_fe_reset(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
15369{
15370	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
15371
15372	if (opline->op1_type == IS_CONST) {
15373		zval *zv = RT_CONSTANT(opline, opline->op1);
15374
15375		|	ZVAL_COPY_CONST res_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_R0
15376		if (Z_REFCOUNTED_P(zv)) {
15377			|	ADDREF_CONST zv, r0
15378		}
15379	} else {
15380		zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
15381
15382		|	// ZVAL_COPY(res, value);
15383		|	ZVAL_COPY_VALUE res_addr, -1, op1_addr, op1_info, ZREG_R0, ZREG_FCARG1
15384		if (opline->op1_type == IS_CV) {
15385			|	TRY_ADDREF op1_info, ah, FCARG1a
15386		}
15387	}
15388	|	// Z_FE_POS_P(res) = 0;
15389	|	mov dword [FP + opline->result.var + offsetof(zval, u2.fe_pos)], 0
15390
15391	return 1;
15392}
15393
15394static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint32_t op2_info, unsigned int target_label, uint8_t exit_opcode, const void *exit_addr)
15395{
15396	zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
15397
15398	if (!MAY_BE_HASH(op1_info) && !MAY_BE_PACKED(op1_info)) {
15399		/* empty array */
15400		if (exit_addr) {
15401			if (exit_opcode == ZEND_JMP) {
15402				|	jmp &exit_addr
15403			}
15404		} else {
15405			|	jmp =>target_label
15406		}
15407		return 1;
15408	}
15409
15410	|	// array = EX_VAR(opline->op1.var);
15411	|	// fe_ht = Z_ARRVAL_P(array);
15412	|	GET_ZVAL_PTR FCARG1a, op1_addr
15413
15414	if (op1_info & MAY_BE_PACKED_GUARD) {
15415		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
15416		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15417
15418		if (!exit_addr) {
15419			return 0;
15420		}
15421		if (op1_info & MAY_BE_ARRAY_PACKED) {
15422			|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
15423			|	jz &exit_addr
15424		} else {
15425			|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
15426			|	jnz &exit_addr
15427		}
15428	}
15429
15430	|	// pos = Z_FE_POS_P(array);
15431	|	mov eax, dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)]
15432
15433	if (MAY_BE_HASH(op1_info)) {
15434		if (MAY_BE_PACKED(op1_info)) {
15435			|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
15436			|	jnz >2
15437		}
15438
15439		|	// p = fe_ht->arData + pos;
15440		|.if X64
15441			||	ZEND_ASSERT(sizeof(Bucket) == 32);
15442			|	mov FCARG2d, eax
15443			|	shl FCARG2a, 5
15444		|.else
15445			|	imul FCARG2a, r0, sizeof(Bucket)
15446		|.endif
15447		|	add FCARG2a, aword [FCARG1a + offsetof(zend_array, arData)]
15448		|1:
15449		|	// if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
15450		|	cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], eax
15451		|	// ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
15452		|   // ZEND_VM_CONTINUE();
15453		if (exit_addr) {
15454			if (exit_opcode == ZEND_JMP) {
15455				|	jbe &exit_addr
15456			} else {
15457				|	jbe >3
15458			}
15459		} else {
15460			|	jbe =>target_label
15461		}
15462		|	// pos++;
15463		|	add eax, 1
15464		|	// value_type = Z_TYPE_INFO_P(value);
15465		|	// if (EXPECTED(value_type != IS_UNDEF)) {
15466		if (!exit_addr || exit_opcode == ZEND_JMP) {
15467			|	IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, >3
15468		} else {
15469			|	IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, &exit_addr
15470		}
15471		|	// p++;
15472		|	add FCARG2a, sizeof(Bucket)
15473		|	jmp <1
15474		if (MAY_BE_PACKED(op1_info)) {
15475			|2:
15476		}
15477	}
15478	if (MAY_BE_PACKED(op1_info)) {
15479		|	// p = fe_ht->arPacked + pos;
15480		||	ZEND_ASSERT(sizeof(zval) == 16);
15481		|	mov FCARG2d, eax
15482		|	shl FCARG2a, 4
15483		|	add FCARG2a, aword [FCARG1a + offsetof(zend_array, arPacked)]
15484		|1:
15485		|	// if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
15486		|	cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], eax
15487		|	// ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
15488		|   // ZEND_VM_CONTINUE();
15489		if (exit_addr) {
15490			if (exit_opcode == ZEND_JMP) {
15491				|	jbe &exit_addr
15492			} else {
15493				|	jbe >4
15494			}
15495		} else {
15496			|	jbe =>target_label
15497		}
15498		|	// pos++;
15499		|	add eax, 1
15500		|	// value_type = Z_TYPE_INFO_P(value);
15501		|	// if (EXPECTED(value_type != IS_UNDEF)) {
15502		if (!exit_addr || exit_opcode == ZEND_JMP) {
15503			|	IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, >4
15504		} else {
15505			|	IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, &exit_addr
15506		}
15507		|	// p++;
15508		|	add FCARG2a, sizeof(zval)
15509		|	jmp <1
15510	}
15511
15512
15513	if (!exit_addr || exit_opcode == ZEND_JMP) {
15514		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
15515		zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
15516		uint32_t val_info;
15517
15518		if (RETURN_VALUE_USED(opline)) {
15519			zend_jit_addr res_addr = RES_ADDR();
15520
15521			if (MAY_BE_HASH(op1_info)) {
15522				|3:
15523				|	// Z_FE_POS_P(array) = pos + 1;
15524				|	mov dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)], eax
15525
15526				if ((op1_info & MAY_BE_ARRAY_KEY_LONG)
15527				 && (op1_info & MAY_BE_ARRAY_KEY_STRING)) {
15528					|	// if (!p->key) {
15529					|	cmp aword [FCARG2a + offsetof(Bucket, key)], 0
15530					|	jz >2
15531				}
15532				if (op1_info & MAY_BE_ARRAY_KEY_STRING) {
15533					|	// ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key);
15534					|	mov r0, aword [FCARG2a + offsetof(Bucket, key)]
15535					|	SET_ZVAL_PTR res_addr, r0
15536					|	test dword [r0 + offsetof(zend_refcounted, gc.u.type_info)], IS_STR_INTERNED
15537					|	jz >1
15538					|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING
15539					|	jmp >3
15540					|1:
15541					|	GC_ADDREF r0
15542					|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX
15543
15544					if ((op1_info & MAY_BE_ARRAY_KEY_LONG) || MAY_BE_PACKED(op1_info)) {
15545					    |	jmp >3
15546						|2:
15547					}
15548				}
15549				if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
15550					|	// ZVAL_LONG(EX_VAR(opline->result.var), p->h);
15551					|	mov r0, aword [FCARG2a + offsetof(Bucket, h)]
15552					|	SET_ZVAL_LVAL res_addr, r0
15553					|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
15554					if (MAY_BE_PACKED(op1_info)) {
15555					    |	jmp >3
15556					}
15557				}
15558			}
15559			if (MAY_BE_PACKED(op1_info)) {
15560				|4:
15561				|	// Z_FE_POS_P(array) = pos + 1;
15562				|	mov dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)], eax
15563				|	sub r0, 1
15564				|	SET_ZVAL_LVAL res_addr, r0
15565				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
15566			}
15567			|3:
15568		} else {
15569			|3:
15570			|4:
15571			|	// Z_FE_POS_P(array) = pos + 1;
15572			|	mov dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)], eax
15573		}
15574
15575		val_info = ((op1_info & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
15576		if (val_info & MAY_BE_ARRAY) {
15577			val_info |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
15578		}
15579		if (op1_info & MAY_BE_ARRAY_OF_REF) {
15580			val_info |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY |
15581				MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
15582		} else if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
15583			val_info |= MAY_BE_RC1 | MAY_BE_RCN;
15584		}
15585
15586		if (opline->op2_type == IS_CV) {
15587			|	// zend_assign_to_variable(variable_ptr, value, IS_CV, EX_USES_STRICT_TYPES());
15588			if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, op2_info, -1, IS_CV, val_addr, val_info, 0, 1)) {
15589				return 0;
15590			}
15591		} else {
15592			|	// ZVAL_COPY(res, value);
15593			|	ZVAL_COPY_VALUE var_addr, -1, val_addr, val_info, ZREG_R0, ZREG_FCARG1
15594			|	TRY_ADDREF val_info, ah, FCARG1a
15595		}
15596	} else {
15597		|3:
15598        |4:
15599	}
15600
15601	return 1;
15602}
15603
15604static int zend_jit_fetch_constant(dasm_State          **Dst,
15605                                   const zend_op        *opline,
15606                                   const zend_op_array  *op_array,
15607                                   zend_ssa             *ssa,
15608                                   const zend_ssa_op    *ssa_op,
15609                                   zend_jit_addr         res_addr)
15610{
15611	zval *zv = RT_CONSTANT(opline, opline->op2) + 1;
15612	zend_jit_addr const_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
15613	uint32_t res_info = RES_INFO();
15614
15615	|	// c = CACHED_PTR(opline->extended_value);
15616	|	mov FCARG1a, EX->run_time_cache
15617	|	mov r0, aword [FCARG1a + opline->extended_value]
15618	|	// if (c != NULL)
15619	|	test r0, r0
15620	|	jz >9
15621	if (!zend_jit_is_persistent_constant(zv, opline->op1.num)) {
15622		|	// if (!IS_SPECIAL_CACHE_VAL(c))
15623		|	test r0, CACHE_SPECIAL
15624		|	jnz >9
15625	}
15626	|8:
15627
15628	if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) {
15629		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
15630		uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
15631		int32_t exit_point;
15632		const void *exit_addr = NULL;
15633
15634		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
15635		SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
15636		exit_point = zend_jit_trace_get_exit_point(opline+1, 0);
15637		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
15638		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15639		if (!exit_addr) {
15640			return 0;
15641		}
15642		res_info &= ~MAY_BE_GUARD;
15643		ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
15644
15645		uint8_t type = concrete_type(res_info);
15646
15647		if (type < IS_STRING) {
15648			|	IF_NOT_ZVAL_TYPE const_addr, type, &exit_addr
15649		} else {
15650			|	GET_ZVAL_TYPE_INFO edx, const_addr
15651			|	IF_NOT_TYPE dl, type, &exit_addr
15652		}
15653		|	ZVAL_COPY_VALUE_V res_addr, -1, const_addr, res_info, ZREG_R0, ZREG_R1
15654		if (type < IS_STRING) {
15655			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
15656				|	SET_ZVAL_TYPE_INFO res_addr, type
15657			} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
15658				return 0;
15659			}
15660		} else {
15661			|	SET_ZVAL_TYPE_INFO res_addr, edx
15662			|	TRY_ADDREF res_info, dh, r1
15663		}
15664	} else {
15665		|	// ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value); (no dup)
15666		|	ZVAL_COPY_VALUE res_addr, MAY_BE_ANY, const_addr, MAY_BE_ANY, ZREG_R0, ZREG_R1
15667		|	TRY_ADDREF MAY_BE_ANY, ah, r1
15668	}
15669
15670	|.cold_code
15671	|9:
15672	|	// SAVE_OPLINE();
15673	|	SET_EX_OPLINE opline, r0
15674	|	// zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num OPLINE_CC EXECUTE_DATA_CC);
15675	|	LOAD_ADDR FCARG1a, zv
15676	|	mov FCARG2a, opline->op1.num
15677	|	EXT_CALL zend_jit_get_constant, r0
15678	|	// ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
15679	|	test r0, r0
15680	|	jnz <8
15681	|	jmp ->exception_handler
15682	|.code
15683
15684	return 1;
15685}
15686
15687static int zend_jit_in_array(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr,  uint8_t smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
15688{
15689	HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
15690	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
15691
15692	ZEND_ASSERT(opline->op1_type != IS_VAR && opline->op1_type != IS_TMP_VAR);
15693	ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_STRING);
15694
15695	|	// result = zend_hash_find_ex(ht, Z_STR_P(op1), OP1_TYPE == IS_CONST);
15696	|	LOAD_ADDR FCARG1a, ht
15697	if (opline->op1_type != IS_CONST) {
15698		|	GET_ZVAL_PTR FCARG2a, op1_addr
15699		|	EXT_CALL zend_hash_find, r0
15700	} else {
15701		zend_string *str = Z_STR_P(RT_CONSTANT(opline, opline->op1));
15702		|	LOAD_ADDR FCARG2a, str
15703		|	EXT_CALL zend_hash_find_known_hash, r0
15704	}
15705	|	test r0, r0
15706	if (exit_addr) {
15707		if (smart_branch_opcode == ZEND_JMPZ) {
15708			|	jz &exit_addr
15709		} else {
15710			|	jnz &exit_addr
15711		}
15712	} else if (smart_branch_opcode) {
15713		if (smart_branch_opcode == ZEND_JMPZ) {
15714			|	jz =>target_label
15715		} else if (smart_branch_opcode == ZEND_JMPNZ) {
15716			|	jnz =>target_label
15717		} else {
15718			ZEND_UNREACHABLE();
15719		}
15720	} else {
15721		|	setnz al
15722		|	movzx eax, al
15723		|	lea eax, [eax + IS_FALSE]
15724		|	SET_ZVAL_TYPE_INFO res_addr, eax
15725	}
15726
15727	return 1;
15728}
15729
15730static int zend_jit_rope(dasm_State **Dst, const zend_op *opline, uint32_t op2_info)
15731{
15732	uint32_t offset;
15733
15734	offset = (opline->opcode == ZEND_ROPE_INIT) ?
15735		opline->result.var :
15736		opline->op1.var + opline->extended_value * sizeof(zend_string*);
15737
15738	if (opline->op2_type == IS_CONST) {
15739		zval *zv = RT_CONSTANT(opline, opline->op2);
15740		zend_string *str;
15741
15742		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
15743		str = Z_STR_P(zv);
15744		|	ADDR_STORE aword [FP + offset], str, r0
15745	} else {
15746		zend_jit_addr op2_addr = OP2_ADDR();
15747
15748		ZEND_ASSERT((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
15749
15750		|	GET_ZVAL_PTR r1, op2_addr
15751		|	mov aword [FP + offset], r1
15752		if (opline->op2_type == IS_CV) {
15753			|	GET_ZVAL_TYPE_INFO eax, op2_addr
15754			|	TRY_ADDREF op2_info, ah, r1
15755		}
15756	}
15757
15758	if (opline->opcode == ZEND_ROPE_END) {
15759		zend_jit_addr res_addr = RES_ADDR();
15760
15761		|	lea FCARG1a, [FP + opline->op1.var]
15762		|	mov FCARG2d, opline->extended_value
15763		|	EXT_CALL zend_jit_rope_end, r0
15764		|	SET_ZVAL_PTR res_addr, r0
15765		|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX
15766	}
15767
15768	return 1;
15769}
15770
15771static bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_addr)
15772{
15773	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
15774	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15775
15776	if (!exit_addr) {
15777		return 0;
15778	}
15779	|	IF_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr
15780
15781	return 1;
15782}
15783
15784static 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)
15785{
15786	zend_jit_addr var_addr = *var_addr_ptr;
15787	uint32_t var_info = *var_info_ptr;
15788	const void *exit_addr = NULL;
15789
15790	if (add_ref_guard || add_type_guard) {
15791		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
15792
15793		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15794		if (!exit_addr) {
15795			return 0;
15796		}
15797	}
15798
15799	if (add_ref_guard) {
15800		|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr
15801	}
15802	if (opline->opcode == ZEND_INIT_METHOD_CALL && opline->op1_type == IS_VAR) {
15803		/* Hack: Convert reference to regular value to simplify JIT code for INIT_METHOD_CALL */
15804		if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
15805			|	LOAD_ZVAL_ADDR FCARG1a, var_addr
15806		}
15807		|	EXT_CALL zend_jit_unref_helper, r0
15808	} else {
15809		|	GET_ZVAL_PTR FCARG1a, var_addr
15810		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, offsetof(zend_reference, val));
15811		*var_addr_ptr = var_addr;
15812	}
15813
15814	if (var_type != IS_UNKNOWN) {
15815		var_type &= ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED);
15816	}
15817	if (add_type_guard
15818	 && var_type != IS_UNKNOWN
15819	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
15820		|	IF_NOT_ZVAL_TYPE var_addr, var_type, &exit_addr
15821
15822		ZEND_ASSERT(var_info & (1 << var_type));
15823		if (var_type < IS_STRING) {
15824			var_info = (1 << var_type);
15825		} else if (var_type != IS_ARRAY) {
15826			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
15827		} else {
15828			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));
15829		}
15830
15831		*var_info_ptr = var_info;
15832	} else {
15833		var_info &= ~MAY_BE_REF;
15834		*var_info_ptr = var_info;
15835	}
15836	*var_info_ptr |= MAY_BE_GUARD; /* prevent generation of specialized zval dtor */
15837
15838	return 1;
15839}
15840
15841static 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)
15842{
15843	zend_jit_addr var_addr = *var_addr_ptr;
15844	uint32_t var_info = *var_info_ptr;
15845	int32_t exit_point;
15846	const void *exit_addr;
15847
15848	if (add_indirect_guard) {
15849		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
15850		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15851
15852		if (!exit_addr) {
15853			return 0;
15854		}
15855		|	IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr
15856		|	GET_ZVAL_PTR FCARG1a, var_addr
15857	} else {
15858		/* May be already loaded into FCARG1a or RAX by previous FETCH_OBJ_W/DIM_W */
15859		if (opline->op1_type != IS_VAR ||
15860				(opline-1)->result_type != IS_VAR  ||
15861				(opline-1)->result.var != opline->op1.var ||
15862				(opline-1)->op1_type == IS_VAR ||
15863				(opline-1)->op2_type == IS_VAR ||
15864				(opline-1)->op2_type == IS_TMP_VAR) {
15865			|	GET_ZVAL_PTR FCARG1a, var_addr
15866		} else if ((opline-1)->opcode == ZEND_FETCH_DIM_W || (opline-1)->opcode == ZEND_FETCH_DIM_RW) {
15867			|	mov FCARG1a, r0
15868		}
15869	}
15870	*var_info_ptr &= ~MAY_BE_INDIRECT;
15871	var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
15872	*var_addr_ptr = var_addr;
15873
15874	if (var_type != IS_UNKNOWN) {
15875		var_type &= ~(IS_TRACE_INDIRECT|IS_TRACE_PACKED);
15876	}
15877	if (!(var_type & IS_TRACE_REFERENCE)
15878	 && var_type != IS_UNKNOWN
15879	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
15880		exit_point = zend_jit_trace_get_exit_point(opline, 0);
15881		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15882
15883		if (!exit_addr) {
15884			return 0;
15885		}
15886
15887		|	IF_NOT_Z_TYPE FCARG1a, var_type, &exit_addr
15888
15889		//var_info = zend_jit_trace_type_to_info_ex(var_type, var_info);
15890		ZEND_ASSERT(var_info & (1 << var_type));
15891		if (var_type < IS_STRING) {
15892			var_info = (1 << var_type);
15893		} else if (var_type != IS_ARRAY) {
15894			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
15895		} else {
15896			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));
15897		}
15898
15899		*var_info_ptr = var_info;
15900	}
15901
15902	return 1;
15903}
15904
15905static 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)
15906{
15907	if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) {
15908		return 0;
15909	}
15910
15911	switch (opline->opcode) {
15912		case ZEND_QM_ASSIGN:
15913		case ZEND_SEND_VAR:
15914		case ZEND_ASSIGN:
15915		case ZEND_PRE_INC:
15916		case ZEND_PRE_DEC:
15917		case ZEND_POST_INC:
15918		case ZEND_POST_DEC:
15919			return 1;
15920		case ZEND_ADD:
15921		case ZEND_SUB:
15922		case ZEND_MUL:
15923		case ZEND_BW_OR:
15924		case ZEND_BW_AND:
15925		case ZEND_BW_XOR:
15926			if (def_var == ssa_op->result_def &&
15927			    use_var == ssa_op->op1_use) {
15928				return 1;
15929			}
15930			break;
15931		default:
15932			break;
15933	}
15934	return 0;
15935}
15936
15937static 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)
15938{
15939	uint32_t op1_info, op2_info;
15940
15941	switch (opline->opcode) {
15942		case ZEND_SEND_VAR:
15943		case ZEND_SEND_VAL:
15944		case ZEND_SEND_VAL_EX:
15945			return (opline->op2_type != IS_CONST);
15946		case ZEND_QM_ASSIGN:
15947		case ZEND_IS_SMALLER:
15948		case ZEND_IS_SMALLER_OR_EQUAL:
15949		case ZEND_IS_EQUAL:
15950		case ZEND_IS_NOT_EQUAL:
15951		case ZEND_IS_IDENTICAL:
15952		case ZEND_IS_NOT_IDENTICAL:
15953		case ZEND_CASE:
15954			return 1;
15955		case ZEND_RETURN:
15956			return (op_array->type != ZEND_EVAL_CODE && op_array->function_name);
15957		case ZEND_ASSIGN:
15958			op1_info = OP1_INFO();
15959			op2_info = OP2_INFO();
15960			return
15961				opline->op1_type == IS_CV &&
15962				!(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_RESOURCE|MAY_BE_OBJECT|MAY_BE_REF)) &&
15963				!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)));
15964		case ZEND_ADD:
15965		case ZEND_SUB:
15966		case ZEND_MUL:
15967			op1_info = OP1_INFO();
15968			op2_info = OP2_INFO();
15969			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE)));
15970		case ZEND_BW_OR:
15971		case ZEND_BW_AND:
15972		case ZEND_BW_XOR:
15973		case ZEND_SL:
15974		case ZEND_SR:
15975		case ZEND_MOD:
15976			op1_info = OP1_INFO();
15977			op2_info = OP2_INFO();
15978			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG));
15979		case ZEND_PRE_INC:
15980		case ZEND_PRE_DEC:
15981		case ZEND_POST_INC:
15982		case ZEND_POST_DEC:
15983			op1_info = OP1_INFO();
15984			op2_info = OP1_DEF_INFO();
15985			return opline->op1_type == IS_CV
15986				&& !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG))
15987				&& (op2_info & MAY_BE_LONG);
15988		case ZEND_STRLEN:
15989			op1_info = OP1_INFO();
15990			return (opline->op1_type & (IS_CV|IS_CONST))
15991				&& (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_STRING;
15992		case ZEND_COUNT:
15993			op1_info = OP1_INFO();
15994			return (opline->op1_type & (IS_CV|IS_CONST))
15995				&& (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_ARRAY;
15996		case ZEND_JMPZ:
15997		case ZEND_JMPNZ:
15998			if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
15999				if (!ssa->cfg.map) {
16000					return 0;
16001				}
16002				if (opline > op_array->opcodes + ssa->cfg.blocks[ssa->cfg.map[opline-op_array->opcodes]].start &&
16003				    ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
16004					return 0;
16005				}
16006			}
16007			ZEND_FALLTHROUGH;
16008		case ZEND_BOOL:
16009		case ZEND_BOOL_NOT:
16010		case ZEND_JMPZ_EX:
16011		case ZEND_JMPNZ_EX:
16012			return 1;
16013		case ZEND_FETCH_CONSTANT:
16014			return 1;
16015		case ZEND_FETCH_DIM_R:
16016			op1_info = OP1_INFO();
16017			op2_info = OP2_INFO();
16018			if (trace
16019			 && trace->op1_type != IS_UNKNOWN
16020			 && (trace->op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)) == IS_ARRAY) {
16021				op1_info &= ~((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY);
16022			}
16023			return ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) &&
16024				(!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || !(op1_info & MAY_BE_RC1)) &&
16025					(((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) ||
16026					 (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) &&
16027						 (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & MAY_BE_RC1))));
16028	}
16029	return 0;
16030}
16031
16032static bool zend_jit_var_supports_reg(zend_ssa *ssa, int var)
16033{
16034	if (ssa->vars[var].no_val) {
16035		/* we don't need the value */
16036		return 0;
16037	}
16038
16039	if (!(JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL)) {
16040		/* Disable global register allocation,
16041		 * register allocation for SSA variables connected through Phi functions
16042		 */
16043		if (ssa->vars[var].definition_phi) {
16044			return 0;
16045		}
16046		if (ssa->vars[var].phi_use_chain) {
16047			zend_ssa_phi *phi = ssa->vars[var].phi_use_chain;
16048			do {
16049				if (!ssa->vars[phi->ssa_var].no_val) {
16050					return 0;
16051				}
16052				phi = zend_ssa_next_use_phi(ssa, var, phi);
16053			} while (phi);
16054		}
16055	}
16056
16057	if (((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_DOUBLE) &&
16058	    ((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_LONG)) {
16059	    /* bad type */
16060		return 0;
16061	}
16062
16063	return 1;
16064}
16065
16066static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, int var)
16067{
16068	if (!zend_jit_var_supports_reg(ssa, var)) {
16069		return 0;
16070	}
16071
16072	if (ssa->vars[var].definition >= 0) {
16073		uint32_t def = ssa->vars[var].definition;
16074		if (!zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + def, ssa->ops + def, NULL)) {
16075			return 0;
16076		}
16077	}
16078
16079	if (ssa->vars[var].use_chain >= 0) {
16080		int use = ssa->vars[var].use_chain;
16081
16082		do {
16083			if (!zend_ssa_is_no_val_use(op_array->opcodes + use, ssa->ops + use, var) &&
16084			    !zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + use, ssa->ops + use, NULL)) {
16085				return 0;
16086			}
16087			use = zend_ssa_next_use(ssa->ops, var, use);
16088		} while (use >= 0);
16089	}
16090
16091	return 1;
16092}
16093
16094static bool zend_needs_extra_reg_for_const(const zend_op *opline, uint8_t op_type, znode_op op)
16095{
16096|.if X64
16097||	if (op_type == IS_CONST) {
16098||		zval *zv = RT_CONSTANT(opline, op);
16099||		if (Z_TYPE_P(zv) == IS_DOUBLE && Z_DVAL_P(zv) != 0 && !IS_SIGNED_32BIT(zv)) {
16100||			return 1;
16101||		} else if (Z_TYPE_P(zv) == IS_LONG && !IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
16102||			return 1;
16103||		}
16104||	}
16105|.endif
16106	return 0;
16107}
16108
16109static 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)
16110{
16111	uint32_t op1_info, op2_info;
16112
16113	switch (opline->opcode) {
16114		case ZEND_FETCH_DIM_R:
16115			op1_info = OP1_INFO();
16116			op2_info = OP2_INFO();
16117			if (((opline->op1_type & (IS_TMP_VAR|IS_VAR)) &&
16118			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) ||
16119			    ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) &&
16120			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)))) {
16121				return ZEND_REGSET(ZREG_FCARG1);
16122			}
16123			break;
16124		default:
16125			break;
16126	}
16127
16128	return ZEND_REGSET_EMPTY;
16129}
16130
16131static 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)
16132{
16133	uint32_t op1_info, op2_info, res_info;
16134	zend_regset regset = ZEND_REGSET_SCRATCH;
16135
16136	switch (opline->opcode) {
16137		case ZEND_NOP:
16138		case ZEND_OP_DATA:
16139		case ZEND_JMP:
16140		case ZEND_RETURN:
16141			regset = ZEND_REGSET_EMPTY;
16142			break;
16143		case ZEND_QM_ASSIGN:
16144			if (ssa_op->op1_def == current_var ||
16145			    ssa_op->result_def == current_var) {
16146				regset = ZEND_REGSET_EMPTY;
16147				break;
16148			}
16149			/* break missing intentionally */
16150		case ZEND_SEND_VAL:
16151		case ZEND_SEND_VAL_EX:
16152			if (opline->op2_type == IS_CONST) {
16153				break;
16154			}
16155			if (ssa_op->op1_use == current_var) {
16156				regset = ZEND_REGSET(ZREG_R0);
16157				break;
16158			}
16159			op1_info = OP1_INFO();
16160			if (!(op1_info & MAY_BE_UNDEF)) {
16161				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
16162					regset = ZEND_REGSET(ZREG_XMM0);
16163				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
16164					regset = ZEND_REGSET(ZREG_R0);
16165				} else {
16166					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_R0), ZEND_REGSET(ZREG_R2));
16167				}
16168			}
16169			break;
16170		case ZEND_SEND_VAR:
16171			if (opline->op2_type == IS_CONST) {
16172				break;
16173			}
16174			if (ssa_op->op1_use == current_var ||
16175			    ssa_op->op1_def == current_var) {
16176				regset = ZEND_REGSET_EMPTY;
16177				break;
16178			}
16179			op1_info = OP1_INFO();
16180			if (!(op1_info & MAY_BE_UNDEF)) {
16181				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
16182					regset = ZEND_REGSET(ZREG_XMM0);
16183				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
16184				} else {
16185					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_R0), ZEND_REGSET(ZREG_R2));
16186					if (op1_info & MAY_BE_REF) {
16187						ZEND_REGSET_INCL(regset, ZREG_R1);
16188					}
16189				}
16190			}
16191			break;
16192		case ZEND_ASSIGN:
16193			if (ssa_op->op2_use == current_var ||
16194			    ssa_op->op2_def == current_var ||
16195			    ssa_op->op1_def == current_var ||
16196			    ssa_op->result_def == current_var) {
16197				regset = ZEND_REGSET_EMPTY;
16198				break;
16199			}
16200			op1_info = OP1_INFO();
16201			op2_info = OP2_INFO();
16202			if (opline->op1_type == IS_CV
16203			 && !(op2_info & MAY_BE_UNDEF)
16204			 && !(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
16205				if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
16206					regset = ZEND_REGSET(ZREG_XMM0);
16207				} else if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
16208					regset = ZEND_REGSET(ZREG_R0);
16209				} else {
16210					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_R0), ZEND_REGSET(ZREG_R2));
16211				}
16212			}
16213			break;
16214		case ZEND_PRE_INC:
16215		case ZEND_PRE_DEC:
16216		case ZEND_POST_INC:
16217		case ZEND_POST_DEC:
16218			if (ssa_op->op1_use == current_var ||
16219			    ssa_op->op1_def == current_var ||
16220			    ssa_op->result_def == current_var) {
16221				regset = ZEND_REGSET_EMPTY;
16222				break;
16223			}
16224			op1_info = OP1_INFO();
16225			if (opline->op1_type == IS_CV
16226			 && (op1_info & MAY_BE_LONG)
16227			 && !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
16228				regset = ZEND_REGSET_EMPTY;
16229				if (op1_info & MAY_BE_DOUBLE) {
16230					regset = ZEND_REGSET(ZREG_XMM0);
16231				}
16232				if (opline->result_type != IS_UNUSED && (op1_info & MAY_BE_LONG)) {
16233					ZEND_REGSET_INCL(regset, ZREG_R1);
16234				}
16235			}
16236			break;
16237		case ZEND_ADD:
16238		case ZEND_SUB:
16239		case ZEND_MUL:
16240			op1_info = OP1_INFO();
16241			op2_info = OP2_INFO();
16242			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
16243			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
16244
16245				regset = ZEND_REGSET_EMPTY;
16246				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
16247					if (ssa_op->result_def != current_var &&
16248					    (ssa_op->op1_use != current_var || !last_use)) {
16249						ZEND_REGSET_INCL(regset, ZREG_R0);
16250					}
16251					res_info = RES_INFO();
16252					if (res_info & MAY_BE_DOUBLE) {
16253						ZEND_REGSET_INCL(regset, ZREG_R0);
16254						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16255						ZEND_REGSET_INCL(regset, ZREG_XMM1);
16256					} else if (res_info & MAY_BE_GUARD) {
16257						ZEND_REGSET_INCL(regset, ZREG_R0);
16258					}
16259				}
16260				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
16261					if (opline->op1_type == IS_CONST) {
16262						ZEND_REGSET_INCL(regset, ZREG_R0);
16263					}
16264					if (ssa_op->result_def != current_var) {
16265						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16266					}
16267				}
16268				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
16269					if (opline->op2_type == IS_CONST) {
16270						ZEND_REGSET_INCL(regset, ZREG_R0);
16271					}
16272					if (zend_is_commutative(opline->opcode)) {
16273						if (ssa_op->result_def != current_var) {
16274							ZEND_REGSET_INCL(regset, ZREG_XMM0);
16275						}
16276					} else {
16277						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16278						if (ssa_op->result_def != current_var &&
16279						    (ssa_op->op1_use != current_var || !last_use)) {
16280							ZEND_REGSET_INCL(regset, ZREG_XMM1);
16281						}
16282					}
16283				}
16284				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
16285					if (ssa_op->result_def != current_var &&
16286					    (ssa_op->op1_use != current_var || !last_use) &&
16287					    (!zend_is_commutative(opline->opcode) || ssa_op->op2_use != current_var || !last_use)) {
16288						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16289					}
16290				}
16291				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
16292				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
16293					if (!ZEND_REGSET_IN(regset, ZREG_R0)) {
16294						ZEND_REGSET_INCL(regset, ZREG_R0);
16295					} else {
16296						ZEND_REGSET_INCL(regset, ZREG_R1);
16297					}
16298				}
16299			}
16300			break;
16301		case ZEND_BW_OR:
16302		case ZEND_BW_AND:
16303		case ZEND_BW_XOR:
16304			op1_info = OP1_INFO();
16305			op2_info = OP2_INFO();
16306			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
16307			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
16308				regset = ZEND_REGSET_EMPTY;
16309				if (ssa_op->result_def != current_var &&
16310				    (ssa_op->op1_use != current_var || !last_use)) {
16311					ZEND_REGSET_INCL(regset, ZREG_R0);
16312				}
16313				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
16314				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
16315					if (!ZEND_REGSET_IN(regset, ZREG_R0)) {
16316						ZEND_REGSET_INCL(regset, ZREG_R0);
16317					} else {
16318						ZEND_REGSET_INCL(regset, ZREG_R1);
16319					}
16320				}
16321			}
16322			break;
16323		case ZEND_SL:
16324		case ZEND_SR:
16325			op1_info = OP1_INFO();
16326			op2_info = OP2_INFO();
16327			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
16328			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
16329bw_op:
16330				regset = ZEND_REGSET_EMPTY;
16331				if (ssa_op->result_def != current_var &&
16332				    (ssa_op->op1_use != current_var || !last_use)) {
16333					ZEND_REGSET_INCL(regset, ZREG_R0);
16334				}
16335				if (opline->op2_type != IS_CONST && ssa_op->op2_use != current_var) {
16336					ZEND_REGSET_INCL(regset, ZREG_R1);
16337				}
16338			}
16339			break;
16340		case ZEND_MOD:
16341			op1_info = OP1_INFO();
16342			op2_info = OP2_INFO();
16343			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
16344			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
16345				if (opline->op2_type == IS_CONST &&
16346				    opline->op1_type != IS_CONST &&
16347				    Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG &&
16348				    zend_long_is_power_of_two(Z_LVAL_P(RT_CONSTANT(opline, opline->op2))) &&
16349				    OP1_HAS_RANGE() &&
16350				    OP1_MIN_RANGE() >= 0) {
16351				    /* MOD is going to be optimized into AND */
16352				    goto bw_op;
16353				} else {
16354					regset = ZEND_REGSET_EMPTY;
16355					ZEND_REGSET_INCL(regset, ZREG_R0);
16356					ZEND_REGSET_INCL(regset, ZREG_R2);
16357					if (opline->op2_type == IS_CONST) {
16358						ZEND_REGSET_INCL(regset, ZREG_R1);
16359					}
16360				}
16361			}
16362			break;
16363		case ZEND_IS_SMALLER:
16364		case ZEND_IS_SMALLER_OR_EQUAL:
16365		case ZEND_IS_EQUAL:
16366		case ZEND_IS_NOT_EQUAL:
16367		case ZEND_IS_IDENTICAL:
16368		case ZEND_IS_NOT_IDENTICAL:
16369		case ZEND_CASE:
16370			op1_info = OP1_INFO();
16371			op2_info = OP2_INFO();
16372			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
16373			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
16374				regset = ZEND_REGSET_EMPTY;
16375				if (!(opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ))) {
16376					ZEND_REGSET_INCL(regset, ZREG_R0);
16377				}
16378				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) &&
16379				    opline->op1_type != IS_CONST && opline->op2_type != IS_CONST) {
16380					if (ssa_op->op1_use != current_var &&
16381					    ssa_op->op2_use != current_var) {
16382						ZEND_REGSET_INCL(regset, ZREG_R0);
16383					}
16384				}
16385				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
16386					ZEND_REGSET_INCL(regset, ZREG_XMM0);
16387				}
16388				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
16389					ZEND_REGSET_INCL(regset, ZREG_XMM0);
16390				}
16391				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
16392					if (ssa_op->op1_use != current_var &&
16393					    ssa_op->op2_use != current_var) {
16394						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16395					}
16396				}
16397				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
16398				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
16399					ZEND_REGSET_INCL(regset, ZREG_R0);
16400				}
16401			}
16402			break;
16403		case ZEND_BOOL:
16404		case ZEND_BOOL_NOT:
16405		case ZEND_JMPZ:
16406		case ZEND_JMPNZ:
16407		case ZEND_JMPZ_EX:
16408		case ZEND_JMPNZ_EX:
16409			op1_info = OP1_INFO();
16410			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)))) {
16411				regset = ZEND_REGSET_EMPTY;
16412				if (op1_info & MAY_BE_DOUBLE) {
16413					ZEND_REGSET_INCL(regset, ZREG_XMM0);
16414				}
16415				if (opline->opcode == ZEND_BOOL ||
16416				    opline->opcode == ZEND_BOOL_NOT ||
16417				    opline->opcode == ZEND_JMPZ_EX ||
16418				    opline->opcode == ZEND_JMPNZ_EX) {
16419					ZEND_REGSET_INCL(regset, ZREG_R0);
16420				}
16421			}
16422			break;
16423		case ZEND_DO_UCALL:
16424		case ZEND_DO_FCALL:
16425		case ZEND_DO_FCALL_BY_NAME:
16426		case ZEND_INCLUDE_OR_EVAL:
16427		case ZEND_GENERATOR_CREATE:
16428		case ZEND_YIELD:
16429		case ZEND_YIELD_FROM:
16430			regset = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP);
16431			break;
16432		default:
16433			break;
16434	}
16435
16436	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
16437		if (ssa_op == ssa->ops
16438		 && JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].op == ZEND_JIT_TRACE_INIT_CALL
16439		 && (JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
16440			ZEND_REGSET_INCL(regset, ZREG_R0);
16441			ZEND_REGSET_INCL(regset, ZREG_R1);
16442		}
16443	}
16444
16445	/* %r0 is used to check EG(vm_interrupt) */
16446	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
16447		if (ssa_op == ssa->ops
16448		 && (JIT_G(current_trace)->stop == ZEND_JIT_TRACE_STOP_LOOP ||
16449			 JIT_G(current_trace)->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL)) {
16450#if ZTS
16451			ZEND_REGSET_INCL(regset, ZREG_R0);
16452#else
16453			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(vm_interrupt)))) {
16454				ZEND_REGSET_INCL(regset, ZREG_R0);
16455			}
16456#endif
16457		}
16458	} else  {
16459		uint32_t b = ssa->cfg.map[ssa_op - ssa->ops];
16460
16461		if ((ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) != 0
16462		 && ssa->cfg.blocks[b].start == ssa_op - ssa->ops) {
16463#if ZTS
16464			ZEND_REGSET_INCL(regset, ZREG_R0);
16465#else
16466			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(vm_interrupt)))) {
16467				ZEND_REGSET_INCL(regset, ZREG_R0);
16468			}
16469#endif
16470		}
16471	}
16472
16473	return regset;
16474}
16475
16476/*
16477 * Local variables:
16478 * tab-width: 4
16479 * c-basic-offset: 4
16480 * indent-tabs-mode: t
16481 * End:
16482 */
16483