xref: /PHP-8.1/ext/opcache/jit/zend_jit_x86.dasc (revision fbf4e196)
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|			mov 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||				zend_uchar 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||		zend_uchar 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||			zend_uchar 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
1631static bool reuse_ip = 0;
1632static bool delayed_call_chain = 0;
1633static uint32_t  delayed_call_level = 0;
1634static const zend_op *last_valid_opline = NULL;
1635static bool use_last_vald_opline = 0;
1636static bool track_last_valid_opline = 0;
1637static int jit_return_label = -1;
1638static uint32_t current_trace_num = 0;
1639static uint32_t allowed_opt_flags = 0;
1640
1641static void zend_jit_track_last_valid_opline(void)
1642{
1643	use_last_vald_opline = 0;
1644	track_last_valid_opline = 1;
1645}
1646
1647static void zend_jit_use_last_valid_opline(void)
1648{
1649	if (track_last_valid_opline) {
1650		use_last_vald_opline = 1;
1651		track_last_valid_opline = 0;
1652	}
1653}
1654
1655static bool zend_jit_trace_uses_initial_ip(void)
1656{
1657	return use_last_vald_opline;
1658}
1659
1660static void zend_jit_set_last_valid_opline(const zend_op *target_opline)
1661{
1662	if (!reuse_ip) {
1663		track_last_valid_opline = 0;
1664		last_valid_opline = target_opline;
1665	}
1666}
1667
1668static void zend_jit_reset_last_valid_opline(void)
1669{
1670	track_last_valid_opline = 0;
1671	last_valid_opline = NULL;
1672}
1673
1674static void zend_jit_start_reuse_ip(void)
1675{
1676	zend_jit_reset_last_valid_opline();
1677	reuse_ip = 1;
1678}
1679
1680static int zend_jit_reuse_ip(dasm_State **Dst)
1681{
1682	if (!reuse_ip) {
1683		zend_jit_start_reuse_ip();
1684		|	// call = EX(call);
1685		|	mov RX, EX->call
1686	}
1687	return 1;
1688}
1689
1690static void zend_jit_stop_reuse_ip(void)
1691{
1692	reuse_ip = 0;
1693}
1694
1695static int zend_jit_interrupt_handler_stub(dasm_State **Dst)
1696{
1697	|->interrupt_handler:
1698	|	SAVE_IP
1699	|	//EG(vm_interrupt) = 0;
1700	|	MEM_STORE_ZTS byte, executor_globals, vm_interrupt, 0, r0
1701	|	//if (EG(timed_out)) {
1702	|	MEM_CMP_ZTS byte, executor_globals, timed_out, 0, r0
1703	|	je >1
1704	|	//zend_timeout();
1705	|	EXT_CALL zend_timeout, r0
1706	|1:
1707	|	//} else if (zend_interrupt_function) {
1708	if (zend_interrupt_function) {
1709		|	//zend_interrupt_function(execute_data);
1710		|.if X64
1711			|	mov CARG1, FP
1712			|	EXT_CALL zend_interrupt_function, r0
1713		|.else
1714			|	mov aword A1, FP
1715			|	EXT_CALL zend_interrupt_function, r0
1716		|.endif
1717		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
1718		|	je >1
1719		|	EXT_CALL zend_jit_exception_in_interrupt_handler_helper, r0
1720		|1:
1721		|	//ZEND_VM_ENTER();
1722		|	//execute_data = EG(current_execute_data);
1723		|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
1724		|	LOAD_IP
1725	}
1726	|	//ZEND_VM_CONTINUE()
1727	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1728		|	ADD_HYBRID_SPAD
1729		|	JMP_IP
1730	} else if (GCC_GLOBAL_REGS) {
1731		|	add r4, SPAD // stack alignment
1732		|	JMP_IP
1733	} else {
1734		|	mov FP, aword T2 // restore FP
1735		|	mov RX, aword T3 // restore IP
1736		|	add r4, NR_SPAD // stack alignment
1737		|	mov r0, 1 // ZEND_VM_ENTER
1738		|	ret
1739	}
1740
1741	return 1;
1742}
1743
1744static int zend_jit_exception_handler_stub(dasm_State **Dst)
1745{
1746	|->exception_handler:
1747	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1748		const void *handler = zend_get_opcode_handler_func(EG(exception_op));
1749
1750		|	ADD_HYBRID_SPAD
1751		|	EXT_CALL handler, r0
1752		|	JMP_IP
1753	} else {
1754		const void *handler = EG(exception_op)->handler;
1755
1756		if (GCC_GLOBAL_REGS) {
1757			|	add r4, SPAD // stack alignment
1758			|	EXT_JMP handler, r0
1759		} else {
1760			|	mov FCARG1a, FP
1761			|	EXT_CALL handler, r0
1762			|	mov FP, aword T2 // restore FP
1763			|	mov RX, aword T3 // restore IP
1764			|	add r4, NR_SPAD // stack alignment
1765			|	test eax, eax
1766			|	jl >1
1767			|	mov r0, 1 // ZEND_VM_ENTER
1768			|1:
1769			|	ret
1770		}
1771	}
1772
1773	return 1;
1774}
1775
1776static int zend_jit_exception_handler_undef_stub(dasm_State **Dst)
1777{
1778	|->exception_handler_undef:
1779	|	MEM_LOAD_ZTS r0, aword, executor_globals, opline_before_exception, r0
1780	|	test byte OP:r0->result_type, (IS_TMP_VAR|IS_VAR)
1781	|	jz >1
1782	|	mov eax, dword OP:r0->result.var
1783	|	SET_Z_TYPE_INFO FP + r0, IS_UNDEF
1784	|1:
1785	|	jmp ->exception_handler
1786
1787	return 1;
1788}
1789
1790
1791static int zend_jit_exception_handler_free_op1_op2_stub(dasm_State **Dst)
1792{
1793	|->exception_handler_free_op1_op2:
1794	|	UNDEF_OPLINE_RESULT_IF_USED
1795	|	test byte OP:RX->op1_type, (IS_TMP_VAR|IS_VAR)
1796	|	je >9
1797	|	mov eax, dword OP:RX->op1.var
1798	|	add r0, FP
1799	|	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
1800	|9:
1801	|	test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR)
1802	|	je >9
1803	|	mov eax, dword OP:RX->op2.var
1804	|	add r0, FP
1805	|	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
1806	|9:
1807	|	jmp ->exception_handler
1808	return 1;
1809}
1810
1811static int zend_jit_exception_handler_free_op2_stub(dasm_State **Dst)
1812{
1813	|->exception_handler_free_op2:
1814	|	MEM_LOAD_ZTS RX, aword, executor_globals, opline_before_exception, r0
1815	|	UNDEF_OPLINE_RESULT_IF_USED
1816	|	test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR)
1817	|	je >9
1818	|	mov eax, dword OP:RX->op2.var
1819	|	add r0, FP
1820	|	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
1821	|9:
1822	|	jmp ->exception_handler
1823	return 1;
1824}
1825
1826static int zend_jit_leave_function_stub(dasm_State **Dst)
1827{
1828	|->leave_function_handler:
1829	|	mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)]
1830	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1831		|	test FCARG1d, ZEND_CALL_TOP
1832		|	jnz >1
1833		|	EXT_CALL zend_jit_leave_nested_func_helper, r0
1834		|	ADD_HYBRID_SPAD
1835		|	JMP_IP
1836		|1:
1837		|	EXT_CALL zend_jit_leave_top_func_helper, r0
1838		|	ADD_HYBRID_SPAD
1839		|	JMP_IP
1840	} else {
1841		if (GCC_GLOBAL_REGS) {
1842			|	add r4, SPAD
1843		} else {
1844			|	mov FCARG2a, FP
1845			|	mov FP, aword T2 // restore FP
1846			|	mov RX, aword T3 // restore IP
1847			|	add r4, NR_SPAD
1848		}
1849		|	test FCARG1d, ZEND_CALL_TOP
1850		|	jnz >1
1851		|	EXT_JMP zend_jit_leave_nested_func_helper, r0
1852		|1:
1853		|	EXT_JMP zend_jit_leave_top_func_helper, r0
1854	}
1855
1856	return 1;
1857}
1858
1859static int zend_jit_leave_throw_stub(dasm_State **Dst)
1860{
1861	|->leave_throw_handler:
1862	|	// if (opline->opcode != ZEND_HANDLE_EXCEPTION) {
1863	if (GCC_GLOBAL_REGS) {
1864		|	cmp byte OP:IP->opcode, ZEND_HANDLE_EXCEPTION
1865		|	je >5
1866		|	// EG(opline_before_exception) = opline;
1867		|	MEM_STORE_ZTS aword, executor_globals, opline_before_exception, IP, r0
1868		|5:
1869		|	// opline = EG(exception_op);
1870		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op
1871		|	// HANDLE_EXCEPTION()
1872		|	jmp ->exception_handler
1873	} else {
1874		|	GET_IP FCARG1a
1875		|	cmp byte OP:FCARG1a->opcode, ZEND_HANDLE_EXCEPTION
1876		|	je >5
1877		|	// EG(opline_before_exception) = opline;
1878		|	MEM_STORE_ZTS aword, executor_globals, opline_before_exception, FCARG1a, r0
1879		|5:
1880		|	// opline = EG(exception_op);
1881		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op
1882		|	mov FP, aword T2 // restore FP
1883		|	mov RX, aword T3 // restore IP
1884		|	add r4, NR_SPAD // stack alignment
1885		|	mov r0, 2 // ZEND_VM_LEAVE
1886		|	ret
1887	}
1888
1889	return 1;
1890}
1891
1892static int zend_jit_icall_throw_stub(dasm_State **Dst)
1893{
1894	|->icall_throw_handler:
1895	|	// zend_rethrow_exception(zend_execute_data *execute_data)
1896	|	mov IP, aword EX->opline
1897	|	// if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) {
1898	|	cmp byte OP:IP->opcode, ZEND_HANDLE_EXCEPTION
1899	|	je >1
1900	|	// EG(opline_before_exception) = opline;
1901	|	MEM_STORE_ZTS aword, executor_globals, opline_before_exception, IP, r0
1902	|1:
1903	|	// opline = EG(exception_op);
1904	|	LOAD_IP_ADDR_ZTS executor_globals, exception_op
1905	||	if (GCC_GLOBAL_REGS) {
1906	|		mov aword EX->opline, IP
1907	||	}
1908	|	// HANDLE_EXCEPTION()
1909	|	jmp ->exception_handler
1910
1911	return 1;
1912}
1913
1914static int zend_jit_throw_cannot_pass_by_ref_stub(dasm_State **Dst)
1915{
1916	|->throw_cannot_pass_by_ref:
1917	|	mov r0, EX->opline
1918	|	mov ecx, dword OP:r0->result.var
1919	|	SET_Z_TYPE_INFO RX+r1, IS_UNDEF
1920	|	// last EX(call) frame may be delayed
1921	|	cmp RX, EX->call
1922	|	je >1
1923	|	mov r1, EX->call
1924	|	mov EX:RX->prev_execute_data, r1
1925	|	mov EX->call, RX
1926	|1:
1927	|	mov RX, r0
1928	|	mov FCARG1d, dword OP:r0->op2.num
1929	|	EXT_CALL zend_cannot_pass_by_reference, r0
1930	|	cmp byte OP:RX->op1_type, IS_TMP_VAR
1931	|	jne >9
1932	|	mov eax, dword OP:RX->op1.var
1933	|	add r0, FP
1934	|	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
1935	|9:
1936	|	jmp ->exception_handler
1937
1938	return 1;
1939}
1940
1941static int zend_jit_undefined_offset_ex_stub(dasm_State **Dst)
1942{
1943	|->undefined_offset_ex:
1944	|	SAVE_IP
1945	|	jmp ->undefined_offset
1946
1947	return 1;
1948}
1949
1950static int zend_jit_undefined_offset_stub(dasm_State **Dst)
1951{
1952	|->undefined_offset:
1953	|.if X64WIN
1954		|	sub r4, 0x28
1955	|.elif X64
1956		|	sub r4, 8
1957	|.else
1958		|	sub r4, 12
1959	|.endif
1960	|	mov r0, EX->opline
1961	|	mov ecx, dword OP:r0->result.var
1962	|	cmp byte OP:r0->op2_type, IS_CONST
1963	|	SET_Z_TYPE_INFO FP + r1, IS_NULL
1964	|	jne >2
1965	|.if X64
1966		|	movsxd r1, dword OP:r0->op2.constant
1967		|	add r0, r1
1968	|.else
1969		|	mov r0, aword OP:r0->op2.zv
1970	|.endif
1971	|	jmp >3
1972	|2:
1973	|	mov eax, dword OP:r0->op2.var
1974	|	add r0, FP
1975	|3:
1976	|.if X64WIN
1977		|	mov CARG1, E_WARNING
1978		|	LOAD_ADDR CARG2, "Undefined array key " ZEND_LONG_FMT
1979		|	mov CARG3, aword [r0]
1980		|	EXT_CALL zend_error, r0
1981		|	add r4, 0x28 // stack alignment
1982	|.elif X64
1983		|	mov CARG1, E_WARNING
1984		|	LOAD_ADDR CARG2, "Undefined array key " ZEND_LONG_FMT
1985		|	mov CARG3, aword [r0]
1986		|	EXT_CALL zend_error, r0
1987		|	add r4, 8 // stack alignment
1988	|.else
1989		|	sub r4, 4
1990		|	push aword [r0]
1991		|	push "Undefined array key " ZEND_LONG_FMT
1992		|	push E_WARNING
1993		|	EXT_CALL zend_error, r0
1994		|	add r4, 28
1995	|.endif
1996	|	ret
1997
1998	return 1;
1999}
2000
2001static int zend_jit_undefined_index_ex_stub(dasm_State **Dst)
2002{
2003	|->undefined_index_ex:
2004	|	SAVE_IP
2005	|	jmp ->undefined_index
2006
2007	return 1;
2008}
2009
2010static int zend_jit_undefined_index_stub(dasm_State **Dst)
2011{
2012	|->undefined_index:
2013	|.if X64WIN
2014		|	sub r4, 0x28
2015	|.elif X64
2016		|	sub r4, 8
2017	|.else
2018		|	sub r4, 12
2019	|.endif
2020	|	mov r0, EX->opline
2021	|	mov ecx, dword OP:r0->result.var
2022	|	cmp byte OP:r0->op2_type, IS_CONST
2023	|	SET_Z_TYPE_INFO FP + r1, IS_NULL
2024	|	jne >2
2025	|.if X64
2026		|	movsxd r1, dword OP:r0->op2.constant
2027		|   add r0, r1
2028	|.else
2029		|	mov r0, aword OP:r0->op2.zv
2030	|.endif
2031	|	jmp >3
2032	|2:
2033	|	mov eax, dword OP:r0->op2.var
2034	|	add r0, FP
2035	|3:
2036	|.if X64WIN
2037		|	mov CARG1, E_WARNING
2038		|	LOAD_ADDR CARG2, "Undefined array key \"%s\""
2039		|	mov CARG3, aword [r0]
2040		|	add CARG3, offsetof(zend_string, val)
2041		|	EXT_CALL zend_error, r0
2042		|	add r4, 0x28
2043	|.elif X64
2044		|	mov CARG1, E_WARNING
2045		|	LOAD_ADDR CARG2, "Undefined array key \"%s\""
2046		|	mov CARG3, aword [r0]
2047		|	add CARG3, offsetof(zend_string, val)
2048		|	EXT_CALL zend_error, r0
2049		|	add r4, 8
2050	|.else
2051		|	sub r4, 4
2052		|	mov r0, aword [r0]
2053		|	add r0, offsetof(zend_string, val)
2054		|	push r0
2055		|	push "Undefined array key \"%s\""
2056		|	push E_WARNING
2057		|	EXT_CALL zend_error, r0
2058		|	add r4, 28
2059	|.endif
2060	|	ret
2061
2062	return 1;
2063}
2064
2065static int zend_jit_cannot_add_element_ex_stub(dasm_State **Dst)
2066{
2067	|->cannot_add_element_ex:
2068	|	SAVE_IP
2069	|	jmp ->cannot_add_element
2070
2071	return 1;
2072}
2073
2074static int zend_jit_cannot_add_element_stub(dasm_State **Dst)
2075{
2076	|->cannot_add_element:
2077	|.if X64WIN
2078		|	sub r4, 0x28
2079	|.elif X64
2080		|	sub r4, 8
2081	|.else
2082		|	sub r4, 12
2083	|.endif
2084	|	mov r0, EX->opline
2085	|	cmp byte OP:r0->result_type, IS_UNUSED
2086	|	jz >1
2087	|	mov eax, dword OP:r0->result.var
2088	|	SET_Z_TYPE_INFO FP + r0, IS_NULL
2089	|1:
2090	|.if X64WIN
2091		|	xor CARG1, CARG1
2092		|	LOAD_ADDR CARG2, "Cannot add element to the array as the next element is already occupied"
2093		|	EXT_CALL zend_throw_error, r0
2094		|	add r4, 0x28
2095	|.elif X64
2096		|	xor CARG1, CARG1
2097		|	LOAD_ADDR CARG2, "Cannot add element to the array as the next element is already occupied"
2098		|	EXT_CALL zend_throw_error, r0
2099		|	add r4, 8
2100	|.else
2101		|	sub r4, 8
2102		|	push "Cannot add element to the array as the next element is already occupied"
2103		|	push 0
2104		|	EXT_CALL zend_throw_error, r0
2105		|	add r4, 28
2106	|.endif
2107	|	ret
2108
2109	return 1;
2110}
2111
2112static int zend_jit_undefined_function_stub(dasm_State **Dst)
2113{
2114	|->undefined_function:
2115	|	mov r0, aword EX->opline
2116	|.if X64
2117		|	xor CARG1, CARG1
2118		|	LOAD_ADDR CARG2, "Call to undefined function %s()"
2119		|	movsxd CARG3, dword [r0 + offsetof(zend_op, op2.constant)]
2120		|	mov CARG3, aword [r0 + CARG3]
2121		|	add CARG3, offsetof(zend_string, val)
2122		|	EXT_CALL zend_throw_error, r0
2123	|.else
2124		|	mov r0, aword [r0 + offsetof(zend_op, op2.zv)]
2125		|	mov r0, aword [r0]
2126		|	add r0, offsetof(zend_string, val)
2127		|	mov aword A3, r0
2128		|	mov aword A2, "Call to undefined function %s()"
2129		|	mov aword A1, 0
2130		|	EXT_CALL zend_throw_error, r0
2131	|.endif
2132	|	jmp ->exception_handler
2133	return 1;
2134}
2135
2136static int zend_jit_negative_shift_stub(dasm_State **Dst)
2137{
2138	|->negative_shift:
2139	|	mov RX, EX->opline
2140	|.if X64
2141		|.if WIN
2142		|	LOAD_ADDR CARG1, &zend_ce_arithmetic_error
2143		|	mov CARG1, aword [CARG1]
2144		|.else
2145		|	LOAD_ADDR CARG1, zend_ce_arithmetic_error
2146		|.endif
2147		|	LOAD_ADDR CARG2, "Bit shift by negative number"
2148		|	EXT_CALL zend_throw_error, r0
2149	|.else
2150		|	sub r4, 8
2151		|	push "Bit shift by negative number"
2152		|.if WIN
2153		|	LOAD_ADDR r0, &zend_ce_arithmetic_error
2154		|	push aword [r0]
2155		|.else
2156		|	PUSH_ADDR zend_ce_arithmetic_error, r0
2157		|.endif
2158		|	EXT_CALL zend_throw_error, r0
2159		|	add r4, 16
2160	|.endif
2161	|	jmp ->exception_handler_free_op1_op2
2162	return 1;
2163}
2164
2165static int zend_jit_mod_by_zero_stub(dasm_State **Dst)
2166{
2167	|->mod_by_zero:
2168	|	mov RX, EX->opline
2169	|.if X64
2170		|.if WIN
2171		|	LOAD_ADDR CARG1, &zend_ce_division_by_zero_error
2172		|	mov CARG1, aword [CARG1]
2173		|.else
2174		|	LOAD_ADDR CARG1, zend_ce_division_by_zero_error
2175		|.endif
2176		|	LOAD_ADDR CARG2, "Modulo by zero"
2177		|	EXT_CALL zend_throw_error, r0
2178	|.else
2179		|	sub r4, 8
2180		|	push "Modulo by zero"
2181		|.if WIN
2182		|	LOAD_ADDR r0, &zend_ce_division_by_zero_error
2183		|	push aword [r0]
2184		|.else
2185		|	PUSH_ADDR zend_ce_division_by_zero_error, r0
2186		|.endif
2187		|	EXT_CALL zend_throw_error, r0
2188		|	add r4, 16
2189	|.endif
2190	|	jmp ->exception_handler_free_op1_op2
2191	return 1;
2192}
2193
2194static int zend_jit_invalid_this_stub(dasm_State **Dst)
2195{
2196	|->invalid_this:
2197	|	UNDEF_OPLINE_RESULT
2198	|.if X64
2199		|	xor CARG1, CARG1
2200		|	LOAD_ADDR CARG2, "Using $this when not in object context"
2201		|	EXT_CALL zend_throw_error, r0
2202	|.else
2203		|	sub r4, 8
2204		|	push "Using $this when not in object context"
2205		|	push 0
2206		|	EXT_CALL zend_throw_error, r0
2207		|	add r4, 16
2208	|.endif
2209	|	jmp ->exception_handler
2210	return 1;
2211}
2212
2213static int zend_jit_double_one_stub(dasm_State **Dst)
2214{
2215	|->one:
2216	|.dword 0, 0x3ff00000
2217	return 1;
2218}
2219
2220static int zend_jit_hybrid_runtime_jit_stub(dasm_State **Dst)
2221{
2222	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2223		return 1;
2224	}
2225
2226	|->hybrid_runtime_jit:
2227	|	EXT_CALL zend_runtime_jit, r0
2228	|	JMP_IP
2229	return 1;
2230}
2231
2232static int zend_jit_hybrid_profile_jit_stub(dasm_State **Dst)
2233{
2234	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2235		return 1;
2236	}
2237
2238	|->hybrid_profile_jit:
2239	|	// ++zend_jit_profile_counter;
2240	|	.if X64
2241	|		LOAD_ADDR r0, &zend_jit_profile_counter
2242	|		inc aword [r0]
2243	|	.else
2244	|		inc aword [&zend_jit_profile_counter]
2245	|	.endif
2246	|	// op_array = (zend_op_array*)EX(func);
2247	|	mov r0, EX->func
2248	|	// run_time_cache = EX(run_time_cache);
2249	|	mov r2, EX->run_time_cache
2250	|	// jit_extension = (const void*)ZEND_FUNC_INFO(op_array);
2251	|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2252	|	// ++ZEND_COUNTER_INFO(op_array)
2253	|	inc aword [r2 + zend_jit_profile_counter_rid * sizeof(void*)]
2254	|	// return ((zend_vm_opcode_handler_t)jit_extension->orig_handler)()
2255	|	jmp aword [r0 + offsetof(zend_jit_op_array_extension, orig_handler)]
2256	return 1;
2257}
2258
2259static int zend_jit_hybrid_hot_code_stub(dasm_State **Dst)
2260{
2261	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2262		return 1;
2263	}
2264
2265	|->hybrid_hot_code:
2266	|	mov word [r2], ZEND_JIT_COUNTER_INIT
2267	|	mov FCARG1a, FP
2268	|	GET_IP FCARG2a
2269	|	EXT_CALL zend_jit_hot_func, r0
2270	|	JMP_IP
2271	return 1;
2272}
2273
2274/*
2275 * This code is based Mike Pall's "Hashed profile counters" idea, implemented
2276 * in LuaJIT. The full description may be found in "LuaJIT 2.0 intellectual
2277 * property disclosure and research opportunities" email
2278 * at http://lua-users.org/lists/lua-l/2009-11/msg00089.html
2279 *
2280 * In addition we use a variation of Knuth's multiplicative hash function
2281 * described at https://code.i-harness.com/en/q/a21ce
2282 *
2283 * uint64_t hash(uint64_t x) {
2284 *    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
2285 *    x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
2286 *    x = x ^ (x >> 31);
2287 *    return x;
2288 * }
2289 *
2290 * uint_32_t hash(uint32_t x) {
2291 *    x = ((x >> 16) ^ x) * 0x45d9f3b;
2292 *    x = ((x >> 16) ^ x) * 0x45d9f3b;
2293 *    x = (x >> 16) ^ x;
2294 *    return x;
2295 * }
2296 *
2297 */
2298static int zend_jit_hybrid_hot_counter_stub(dasm_State **Dst, uint32_t cost)
2299{
2300	|	mov r0, EX->func
2301	|	mov r1, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2302	|	mov r2, aword [r1 + offsetof(zend_jit_op_array_hot_extension, counter)]
2303	|	sub word [r2], cost
2304	|	jle ->hybrid_hot_code
2305	|	GET_IP r2
2306	|	sub r2, aword [r0 + offsetof(zend_op_array, opcodes)]
2307	|	// divide by sizeof(zend_op)
2308	|	.if X64
2309	||		ZEND_ASSERT(sizeof(zend_op) == 32);
2310	|		sar r2, 2
2311	|	.else
2312	||		ZEND_ASSERT(sizeof(zend_op) == 28);
2313	|		imul r2, 0xb6db6db7
2314	|	.endif
2315	|	.if X64
2316	|		jmp aword [r1+r2+offsetof(zend_jit_op_array_hot_extension, orig_handlers)]
2317	|	.else
2318	|		jmp aword [r1+r2+offsetof(zend_jit_op_array_hot_extension, orig_handlers)]
2319	|	.endif
2320	return 1;
2321}
2322
2323static int zend_jit_hybrid_func_hot_counter_stub(dasm_State **Dst)
2324{
2325	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
2326		return 1;
2327	}
2328
2329	|->hybrid_func_hot_counter:
2330
2331	return zend_jit_hybrid_hot_counter_stub(Dst,
2332		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func)));
2333}
2334
2335static int zend_jit_hybrid_loop_hot_counter_stub(dasm_State **Dst)
2336{
2337	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
2338		return 1;
2339	}
2340
2341	|->hybrid_loop_hot_counter:
2342
2343	return zend_jit_hybrid_hot_counter_stub(Dst,
2344		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
2345}
2346
2347static int zend_jit_hybrid_hot_trace_stub(dasm_State **Dst)
2348{
2349	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2350		return 1;
2351	}
2352
2353	|->hybrid_hot_trace:
2354	|	mov word [r2], ZEND_JIT_COUNTER_INIT
2355	|	mov FCARG1a, FP
2356	|	GET_IP FCARG2a
2357	|	EXT_CALL zend_jit_trace_hot_root, r0
2358	|	test eax, eax // TODO : remove this check at least for HYBRID VM ???
2359	|	jl >1
2360	|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
2361	|	LOAD_IP
2362	|	JMP_IP
2363	|1:
2364	|	EXT_JMP zend_jit_halt_op->handler, r0
2365	return 1;
2366}
2367
2368static int zend_jit_hybrid_trace_counter_stub(dasm_State **Dst, uint32_t cost)
2369{
2370	|	mov r0, EX->func
2371	|	mov r1, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2372	|	mov r1, aword [r1 + offsetof(zend_jit_op_array_trace_extension, offset)]
2373	|	mov r2, aword [IP + r1 + offsetof(zend_op_trace_info, counter)]
2374	|	sub word [r2], cost
2375	|	jle ->hybrid_hot_trace
2376	|	jmp aword [IP + r1]
2377	return 1;
2378}
2379
2380static int zend_jit_hybrid_func_trace_counter_stub(dasm_State **Dst)
2381{
2382	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
2383		return 1;
2384	}
2385
2386	|->hybrid_func_trace_counter:
2387
2388	return zend_jit_hybrid_trace_counter_stub(Dst,
2389		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1)  / JIT_G(hot_func)));
2390}
2391
2392static int zend_jit_hybrid_ret_trace_counter_stub(dasm_State **Dst)
2393{
2394	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_return)) {
2395		return 1;
2396	}
2397
2398	|->hybrid_ret_trace_counter:
2399
2400	return zend_jit_hybrid_trace_counter_stub(Dst,
2401		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_return) - 1) / JIT_G(hot_return)));
2402}
2403
2404static int zend_jit_hybrid_loop_trace_counter_stub(dasm_State **Dst)
2405{
2406	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
2407		return 1;
2408	}
2409
2410	|->hybrid_loop_trace_counter:
2411
2412	return zend_jit_hybrid_trace_counter_stub(Dst,
2413		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
2414}
2415
2416static int zend_jit_trace_halt_stub(dasm_State **Dst)
2417{
2418	|->trace_halt:
2419	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2420		|	ADD_HYBRID_SPAD
2421		|	EXT_JMP zend_jit_halt_op->handler, r0
2422	} else if (GCC_GLOBAL_REGS) {
2423		|	add r4, SPAD // stack alignment
2424		|	xor IP, IP // PC must be zero
2425		|	ret
2426	} else {
2427		|	mov FP, aword T2 // restore FP
2428		|	mov RX, aword T3 // restore IP
2429		|	add r4, NR_SPAD // stack alignment
2430		|	mov r0, -1 // ZEND_VM_RETURN
2431		|	ret
2432	}
2433	return 1;
2434}
2435
2436static int zend_jit_trace_exit_stub(dasm_State **Dst)
2437{
2438	|->trace_exit:
2439	|
2440	|	// Save CPU registers
2441	|.if X64
2442	|	sub r4, 16*8+16*8-8 /* CPU regs + SSE regs */
2443	|	mov aword [r4+15*8], r15
2444	|	mov aword [r4+11*8], r11
2445	|	mov aword [r4+10*8], r10
2446	|	mov aword [r4+9*8], r9
2447	|	mov aword [r4+8*8], r8
2448	|	mov aword [r4+7*8], rdi
2449	|	mov aword [r4+6*8], rsi
2450	|	mov aword [r4+2*8], rdx
2451	|	mov aword [r4+1*8], rcx
2452	|	mov aword [r4+0*8], rax
2453	|	mov FCARG1a, aword [r4+16*8+16*8-8] // exit_num = POP
2454	|	mov FCARG2a, r4
2455	|	movsd qword [r4+16*8+15*8], xmm15
2456	|	movsd qword [r4+16*8+14*8], xmm14
2457	|	movsd qword [r4+16*8+13*8], xmm13
2458	|	movsd qword [r4+16*8+12*8], xmm12
2459	|	movsd qword [r4+16*8+11*8], xmm11
2460	|	movsd qword [r4+16*8+10*8], xmm10
2461	|	movsd qword [r4+16*8+9*8], xmm9
2462	|	movsd qword [r4+16*8+8*8], xmm8
2463	|	movsd qword [r4+16*8+7*8], xmm7
2464	|	movsd qword [r4+16*8+6*8], xmm6
2465	|	movsd qword [r4+16*8+5*8], xmm5
2466	|	movsd qword [r4+16*8+4*8], xmm4
2467	|	movsd qword [r4+16*8+3*8], xmm3
2468	|	movsd qword [r4+16*8+2*8], xmm2
2469	|	movsd qword [r4+16*8+1*8], xmm1
2470	|	movsd qword [r4+16*8+0*8], xmm0
2471	|.if X64WIN
2472	|	sub r4, 32 /* shadow space */
2473	|.endif
2474	|.else
2475	|	sub r4, 8*4+8*8-4 /* CPU regs + SSE regs */
2476	|	mov aword [r4+7*4], edi
2477	|	mov aword [r4+2*4], edx
2478	|	mov aword [r4+1*4], ecx
2479	|	mov aword [r4+0*4], eax
2480	|	mov FCARG1a, aword [r4+8*4+8*8-4] // exit_num = POP
2481	|	mov FCARG2a, r4
2482	|	movsd qword [r4+8*4+7*8], xmm7
2483	|	movsd qword [r4+8*4+6*8], xmm6
2484	|	movsd qword [r4+8*4+5*8], xmm5
2485	|	movsd qword [r4+8*4+4*8], xmm4
2486	|	movsd qword [r4+8*4+3*8], xmm3
2487	|	movsd qword [r4+8*4+2*8], xmm2
2488	|	movsd qword [r4+8*4+1*8], xmm1
2489	|	movsd qword [r4+8*4+0*8], xmm0
2490	|.endif
2491	|
2492	|	// EX(opline) = opline
2493	|	SAVE_IP
2494	|	// zend_jit_trace_exit(trace_num, exit_num)
2495	|	EXT_CALL zend_jit_trace_exit, r0
2496	|.if X64WIN
2497	|	add r4, 16*8+16*8+32 /* CPU regs + SSE regs + shadow space */
2498	|.elif X64
2499	|	add r4, 16*8+16*8 /* CPU regs + SSE regs */
2500	|.else
2501	|	add r4, 8*4+8*8 /* CPU regs + SSE regs */
2502	|.endif
2503
2504	|	test eax, eax
2505	|	jne >1
2506
2507	|	// execute_data = EG(current_execute_data)
2508	|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
2509	|	// opline = EX(opline)
2510	|	LOAD_IP
2511
2512	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2513		|	ADD_HYBRID_SPAD
2514		|	JMP_IP
2515	} else if (GCC_GLOBAL_REGS) {
2516		|	add r4, SPAD // stack alignment
2517		|	JMP_IP
2518	} else {
2519		|	mov FP, aword T2 // restore FP
2520		|	mov RX, aword T3 // restore IP
2521		|	add r4, NR_SPAD // stack alignment
2522		|	mov r0, 1 // ZEND_VM_ENTER
2523		|	ret
2524	}
2525
2526	|1:
2527	|	jl ->trace_halt
2528
2529	|	// execute_data = EG(current_execute_data)
2530	|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
2531	|	// opline = EX(opline)
2532	|	LOAD_IP
2533
2534	|	// check for interrupt (try to avoid this ???)
2535	|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
2536	|	jne ->interrupt_handler
2537
2538	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2539		|	ADD_HYBRID_SPAD
2540		|	mov r0, EX->func
2541		|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2542		|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
2543		|	jmp aword [IP + r0]
2544	} else if (GCC_GLOBAL_REGS) {
2545		|	add r4, SPAD // stack alignment
2546		|	mov r0, EX->func
2547		|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2548		|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
2549		|	jmp aword [IP + r0]
2550	} else {
2551		|	mov IP, aword EX->opline
2552		|	mov FCARG1a, FP
2553		|	mov r0, EX->func
2554		|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
2555		|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
2556		|	call aword [IP + r0]
2557		|	test eax, eax
2558		|	jl ->trace_halt
2559		|	mov FP, aword T2 // restore FP
2560		|	mov RX, aword T3 // restore IP
2561		|	add r4, NR_SPAD // stack alignment
2562		|	mov r0, 1 // ZEND_VM_ENTER
2563		|	ret
2564	}
2565
2566	return 1;
2567}
2568
2569static int zend_jit_trace_escape_stub(dasm_State **Dst)
2570{
2571	|->trace_escape:
2572	|
2573	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2574		|	ADD_HYBRID_SPAD
2575		|	JMP_IP
2576	} else if (GCC_GLOBAL_REGS) {
2577		|	add r4, SPAD // stack alignment
2578		|	JMP_IP
2579	} else {
2580		|	mov FP, aword T2 // restore FP
2581		|	mov RX, aword T3 // restore IP
2582		|	add r4, NR_SPAD // stack alignment
2583		|	mov r0, 1 // ZEND_VM_ENTER
2584		|	ret
2585	}
2586
2587	return 1;
2588}
2589
2590/* Keep 32 exit points in a single code block */
2591#define ZEND_JIT_EXIT_POINTS_SPACING   4  // push byte + short jmp = bytes
2592#define ZEND_JIT_EXIT_POINTS_PER_GROUP 32 // number of continuous exit points
2593
2594static int zend_jit_trace_exit_group_stub(dasm_State **Dst, uint32_t n)
2595{
2596	uint32_t i;
2597
2598	for (i = 0; i < ZEND_JIT_EXIT_POINTS_PER_GROUP - 1; i++) {
2599		|	push byte i
2600		|	.byte 0xeb, (4*(ZEND_JIT_EXIT_POINTS_PER_GROUP-i)-6) // jmp >1
2601	}
2602	|	push byte i
2603	|// 1:
2604	|	add aword [r4], n
2605	|	jmp ->trace_exit
2606
2607	return 1;
2608}
2609
2610#ifdef CONTEXT_THREADED_JIT
2611static int zend_jit_context_threaded_call_stub(dasm_State **Dst)
2612{
2613	|->context_threaded_call:
2614	|	pop r0
2615	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2616		|	ADD_HYBRID_SPAD
2617		|	jmp aword [IP]
2618	} else if (GCC_GLOBAL_REGS) {
2619		|	add r4, SPAD // stack alignment
2620		|	jmp aword [IP]
2621	} else {
2622		ZEND_UNREACHABLE();
2623		// TODO: context threading can't work without GLOBAL REGS because we have to change
2624		//       the value of execute_data in execute_ex()
2625		|	mov FCARG1a, FP
2626		|	mov r0, aword [FP]
2627		|	mov FP, aword T2 // restore FP
2628		|	mov RX, aword T3 // restore IP
2629		|	add r4, NR_SPAD // stack alignment
2630		|	jmp aword [r0]
2631	}
2632	return 1;
2633}
2634#endif
2635
2636static int zend_jit_assign_const_stub(dasm_State **Dst)
2637{
2638	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2639	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2640	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
2641
2642	|->assign_const:
2643	|.if X64WIN
2644	|	sub r4, 0x28
2645	|.elif X64
2646	|	sub r4, 8
2647	|.else
2648	|	sub r4, 12
2649	|.endif
2650	if (!zend_jit_assign_to_variable(
2651			Dst, NULL,
2652			var_addr, var_addr, -1, -1,
2653			IS_CONST, val_addr, val_info,
2654			0, 0)) {
2655		return 0;
2656	}
2657	|.if X64WIN
2658	|	add r4, 0x28
2659	|.elif X64
2660	|	add r4, 8
2661	|.else
2662	|	add r4, 12
2663	|.endif
2664	|	ret
2665	return 1;
2666}
2667
2668static int zend_jit_assign_tmp_stub(dasm_State **Dst)
2669{
2670	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2671	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2672	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
2673
2674	|->assign_tmp:
2675	|.if X64WIN
2676	|	sub r4, 0x28
2677	|.elif X64
2678	|	sub r4, 8
2679	|.else
2680	|	sub r4, 12
2681	|.endif
2682	if (!zend_jit_assign_to_variable(
2683			Dst, NULL,
2684			var_addr, var_addr, -1, -1,
2685			IS_TMP_VAR, val_addr, val_info,
2686			0, 0)) {
2687		return 0;
2688	}
2689	|.if X64WIN
2690	|	add r4, 0x28
2691	|.elif X64
2692	|	add r4, 8
2693	|.else
2694	|	add r4, 12
2695	|.endif
2696	|	ret
2697	return 1;
2698}
2699
2700static int zend_jit_assign_var_stub(dasm_State **Dst)
2701{
2702	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2703	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2704	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF;
2705
2706	|->assign_var:
2707	|.if X64WIN
2708	|	sub r4, 0x28
2709	|.elif X64
2710	|	sub r4, 8
2711	|.else
2712	|	sub r4, 12
2713	|.endif
2714	if (!zend_jit_assign_to_variable(
2715			Dst, NULL,
2716			var_addr, var_addr, -1, -1,
2717			IS_VAR, val_addr, val_info,
2718			0, 0)) {
2719		return 0;
2720	}
2721	|.if X64WIN
2722	|	add r4, 0x28
2723	|.elif X64
2724	|	add r4, 8
2725	|.else
2726	|	add r4, 12
2727	|.endif
2728	|	ret
2729	return 1;
2730}
2731
2732static int zend_jit_assign_cv_noref_stub(dasm_State **Dst)
2733{
2734	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2735	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2736	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN/*|MAY_BE_UNDEF*/;
2737
2738	|->assign_cv_noref:
2739	|.if X64WIN
2740	|	sub r4, 0x28
2741	|.elif X64
2742	|	sub r4, 8
2743	|.else
2744	|	sub r4, 12
2745	|.endif
2746	if (!zend_jit_assign_to_variable(
2747			Dst, NULL,
2748			var_addr, var_addr, -1, -1,
2749			IS_CV, val_addr, val_info,
2750			0, 0)) {
2751		return 0;
2752	}
2753	|.if X64WIN
2754	|	add r4, 0x28
2755	|.elif X64
2756	|	add r4, 8
2757	|.else
2758	|	add r4, 12
2759	|.endif
2760	|	ret
2761	return 1;
2762}
2763
2764static int zend_jit_assign_cv_stub(dasm_State **Dst)
2765{
2766	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2767	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2768	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF/*|MAY_BE_UNDEF*/;
2769
2770	|->assign_cv:
2771	|.if X64WIN
2772	|	sub r4, 0x28
2773	|.elif X64
2774	|	sub r4, 8
2775	|.else
2776	|	sub r4, 12
2777	|.endif
2778	if (!zend_jit_assign_to_variable(
2779			Dst, NULL,
2780			var_addr, var_addr, -1, -1,
2781			IS_CV, val_addr, val_info,
2782			0, 0)) {
2783		return 0;
2784	}
2785	|.if X64WIN
2786	|	add r4, 0x28
2787	|.elif X64
2788	|	add r4, 8
2789	|.else
2790	|	add r4, 12
2791	|.endif
2792	|	ret
2793	return 1;
2794}
2795
2796static const zend_jit_stub zend_jit_stubs[] = {
2797	JIT_STUB(interrupt_handler,         SP_ADJ_JIT,  SP_ADJ_VM),
2798	JIT_STUB(exception_handler,         SP_ADJ_JIT,  SP_ADJ_VM),
2799	JIT_STUB(exception_handler_undef,   SP_ADJ_JIT,  SP_ADJ_VM),
2800	JIT_STUB(exception_handler_free_op1_op2, SP_ADJ_JIT,  SP_ADJ_VM),
2801	JIT_STUB(exception_handler_free_op2,     SP_ADJ_JIT,  SP_ADJ_VM),
2802	JIT_STUB(leave_function,            SP_ADJ_JIT,  SP_ADJ_VM),
2803	JIT_STUB(leave_throw,               SP_ADJ_JIT,  SP_ADJ_VM),
2804	JIT_STUB(icall_throw,               SP_ADJ_JIT,  SP_ADJ_VM),
2805	JIT_STUB(throw_cannot_pass_by_ref,  SP_ADJ_JIT,  SP_ADJ_VM),
2806	JIT_STUB(undefined_offset,          SP_ADJ_JIT,  SP_ADJ_VM),
2807	JIT_STUB(undefined_index,           SP_ADJ_JIT,  SP_ADJ_VM),
2808	JIT_STUB(cannot_add_element,        SP_ADJ_JIT,  SP_ADJ_VM),
2809	JIT_STUB(undefined_offset_ex,       SP_ADJ_JIT,  SP_ADJ_VM),
2810	JIT_STUB(undefined_index_ex,        SP_ADJ_JIT,  SP_ADJ_VM),
2811	JIT_STUB(cannot_add_element_ex,     SP_ADJ_JIT,  SP_ADJ_VM),
2812	JIT_STUB(undefined_function,        SP_ADJ_JIT,  SP_ADJ_VM),
2813	JIT_STUB(negative_shift,            SP_ADJ_JIT,  SP_ADJ_VM),
2814	JIT_STUB(mod_by_zero,               SP_ADJ_JIT,  SP_ADJ_VM),
2815	JIT_STUB(invalid_this,              SP_ADJ_JIT,  SP_ADJ_VM),
2816	JIT_STUB(trace_halt,                SP_ADJ_JIT,  SP_ADJ_VM),
2817	JIT_STUB(trace_exit,                SP_ADJ_JIT,  SP_ADJ_VM),
2818	JIT_STUB(trace_escape,              SP_ADJ_JIT,  SP_ADJ_VM),
2819	JIT_STUB(hybrid_runtime_jit,        SP_ADJ_VM,   SP_ADJ_NONE),
2820	JIT_STUB(hybrid_profile_jit,        SP_ADJ_VM,   SP_ADJ_NONE),
2821	JIT_STUB(hybrid_hot_code,           SP_ADJ_VM,   SP_ADJ_NONE),
2822	JIT_STUB(hybrid_func_hot_counter,   SP_ADJ_VM,   SP_ADJ_NONE),
2823	JIT_STUB(hybrid_loop_hot_counter,   SP_ADJ_VM,   SP_ADJ_NONE),
2824	JIT_STUB(hybrid_hot_trace,          SP_ADJ_VM,   SP_ADJ_NONE),
2825	JIT_STUB(hybrid_func_trace_counter, SP_ADJ_VM,   SP_ADJ_NONE),
2826	JIT_STUB(hybrid_ret_trace_counter,  SP_ADJ_VM,   SP_ADJ_NONE),
2827	JIT_STUB(hybrid_loop_trace_counter, SP_ADJ_VM,   SP_ADJ_NONE),
2828	JIT_STUB(assign_const,              SP_ADJ_RET,  SP_ADJ_ASSIGN),
2829	JIT_STUB(assign_tmp,                SP_ADJ_RET,  SP_ADJ_ASSIGN),
2830	JIT_STUB(assign_var,                SP_ADJ_RET,  SP_ADJ_ASSIGN),
2831	JIT_STUB(assign_cv_noref,           SP_ADJ_RET,  SP_ADJ_ASSIGN),
2832	JIT_STUB(assign_cv,                 SP_ADJ_RET,  SP_ADJ_ASSIGN),
2833	JIT_STUB(double_one,                SP_ADJ_NONE, SP_ADJ_NONE),
2834#ifdef CONTEXT_THREADED_JIT
2835	JIT_STUB(context_threaded_call,     SP_ADJ_RET,  SP_ADJ_NONE),
2836#endif
2837};
2838
2839#if ZTS && defined(ZEND_WIN32)
2840extern uint32_t _tls_index;
2841extern char *_tls_start;
2842extern char *_tls_end;
2843#endif
2844
2845#ifdef HAVE_GDB
2846typedef struct _Unwind_Context _Unwind_Context;
2847typedef int (*_Unwind_Trace_Fn)(_Unwind_Context *, void *);
2848extern int _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
2849extern uintptr_t _Unwind_GetCFA(_Unwind_Context *);
2850
2851typedef struct _zend_jit_unwind_arg {
2852	int cnt;
2853	uintptr_t cfa[3];
2854} zend_jit_unwind_arg;
2855
2856static int zend_jit_unwind_cb(_Unwind_Context *ctx, void *a)
2857{
2858	zend_jit_unwind_arg *arg = (zend_jit_unwind_arg*)a;
2859	arg->cfa[arg->cnt] = _Unwind_GetCFA(ctx);
2860	arg->cnt++;
2861	if (arg->cnt == 3) {
2862		return 5; // _URC_END_OF_STACK
2863	}
2864	return 0; // _URC_NO_REASON;
2865}
2866
2867static void ZEND_FASTCALL zend_jit_touch_vm_stack_data(void *vm_stack_data)
2868{
2869	zend_jit_unwind_arg arg;
2870
2871	memset(&arg, 0, sizeof(arg));
2872	_Unwind_Backtrace(zend_jit_unwind_cb, &arg);
2873	if (arg.cnt == 3) {
2874		sp_adj[SP_ADJ_VM] = arg.cfa[2] - arg.cfa[1];
2875	}
2876}
2877
2878extern void (ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data);
2879
2880static zend_never_inline void zend_jit_set_sp_adj_vm(void)
2881{
2882	void (ZEND_FASTCALL *orig_zend_touch_vm_stack_data)(void *);
2883
2884	orig_zend_touch_vm_stack_data = zend_touch_vm_stack_data;
2885	zend_touch_vm_stack_data = zend_jit_touch_vm_stack_data;
2886	execute_ex(NULL);                                        // set sp_adj[SP_ADJ_VM]
2887	zend_touch_vm_stack_data = orig_zend_touch_vm_stack_data;
2888}
2889#endif
2890
2891static int zend_jit_setup(void)
2892{
2893	if (!zend_cpu_supports_sse2()) {
2894		zend_error(E_CORE_ERROR, "CPU doesn't support SSE2");
2895		return FAILURE;
2896	}
2897	allowed_opt_flags = 0;
2898	if (zend_cpu_supports_avx()) {
2899		allowed_opt_flags |= ZEND_JIT_CPU_AVX;
2900	}
2901
2902#if ZTS
2903# ifdef _WIN64
2904	tsrm_tls_index  = _tls_index * sizeof(void*);
2905
2906	/* To find offset of "_tsrm_ls_cache" in TLS segment we perform a linear scan of local TLS memory */
2907	/* Probably, it might be better solution */
2908	do {
2909		void ***tls_mem = ((void**)__readgsqword(0x58))[_tls_index];
2910		void *val = _tsrm_ls_cache;
2911		size_t offset = 0;
2912		size_t size = (char*)&_tls_end - (char*)&_tls_start;
2913
2914		while (offset < size) {
2915			if (*tls_mem == val) {
2916				tsrm_tls_offset = offset;
2917				break;
2918			}
2919			tls_mem++;
2920			offset += sizeof(void*);
2921		}
2922		if (offset >= size) {
2923			// TODO: error message ???
2924			return FAILURE;
2925		}
2926	} while(0);
2927# elif ZEND_WIN32
2928	tsrm_tls_index  = _tls_index * sizeof(void*);
2929
2930	/* To find offset of "_tsrm_ls_cache" in TLS segment we perform a linear scan of local TLS memory */
2931	/* Probably, it might be better solution */
2932	do {
2933		void ***tls_mem = ((void***)__readfsdword(0x2c))[_tls_index];
2934		void *val = _tsrm_ls_cache;
2935		size_t offset = 0;
2936		size_t size = (char*)&_tls_end - (char*)&_tls_start;
2937
2938		while (offset < size) {
2939			if (*tls_mem == val) {
2940				tsrm_tls_offset = offset;
2941				break;
2942			}
2943			tls_mem++;
2944			offset += sizeof(void*);
2945		}
2946		if (offset >= size) {
2947			// TODO: error message ???
2948			return FAILURE;
2949		}
2950	} while(0);
2951# elif defined(__APPLE__) && defined(__x86_64__)
2952	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
2953	if (tsrm_ls_cache_tcb_offset == 0) {
2954		size_t *ti;
2955		__asm__(
2956			"leaq __tsrm_ls_cache(%%rip),%0"
2957			: "=r" (ti));
2958		tsrm_tls_offset = ti[2];
2959		tsrm_tls_index = ti[1] * 8;
2960	}
2961# elif defined(__GNUC__) && defined(__x86_64__)
2962	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
2963	if (tsrm_ls_cache_tcb_offset == 0) {
2964#if defined(__has_attribute) && __has_attribute(tls_model) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__)
2965		size_t ret;
2966
2967		asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0"
2968			: "=r" (ret));
2969		tsrm_ls_cache_tcb_offset = ret;
2970#else
2971		size_t *ti;
2972
2973		__asm__(
2974			"leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n"
2975			: "=a" (ti));
2976		tsrm_tls_offset = ti[1];
2977		tsrm_tls_index = ti[0] * 16;
2978#endif
2979	}
2980# elif defined(__GNUC__) && defined(__i386__)
2981	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
2982	if (tsrm_ls_cache_tcb_offset == 0) {
2983#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__)
2984		size_t ret;
2985
2986		asm ("leal _tsrm_ls_cache@ntpoff,%0\n"
2987			: "=a" (ret));
2988		tsrm_ls_cache_tcb_offset = ret;
2989#else
2990		size_t *ti, _ebx, _ecx, _edx;
2991
2992		__asm__(
2993			"call 1f\n"
2994			".subsection 1\n"
2995			"1:\tmovl (%%esp), %%ebx\n\t"
2996			"ret\n"
2997			".previous\n\t"
2998			"addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t"
2999			"leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n\t"
3000			"call ___tls_get_addr@plt\n\t"
3001			"leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n"
3002			: "=a" (ti), "=&b" (_ebx), "=&c" (_ecx), "=&d" (_edx));
3003		tsrm_tls_offset = ti[1];
3004		tsrm_tls_index = ti[0] * 8;
3005#endif
3006	}
3007# endif
3008#endif
3009
3010    memset(sp_adj, 0, sizeof(sp_adj));
3011#ifdef HAVE_GDB
3012	sp_adj[SP_ADJ_RET] = sizeof(void*);
3013	|.if X64WIN
3014	||	sp_adj[SP_ADJ_ASSIGN] = sp_adj[SP_ADJ_RET] + 0x28;       // sub r4, 0x28
3015	|.elif X64
3016	||	sp_adj[SP_ADJ_ASSIGN] = sp_adj[SP_ADJ_RET] + 8;          // sub r4, 8
3017	|.else
3018	||	sp_adj[SP_ADJ_ASSIGN] = sp_adj[SP_ADJ_RET] + 12;         // sub r4, 12
3019	|.endif
3020	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3021		zend_jit_set_sp_adj_vm();                                // set sp_adj[SP_ADJ_VM]
3022#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
3023		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM] + HYBRID_SPAD; // sub r4, HYBRID_SPAD
3024#else
3025		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM];
3026#endif
3027	} else if (GCC_GLOBAL_REGS) {
3028		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + SPAD;       // sub r4, SPAD
3029	} else {
3030		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + NR_SPAD;    // sub r4, NR_SPAD
3031	}
3032#endif
3033
3034	return SUCCESS;
3035}
3036
3037static ZEND_ATTRIBUTE_UNUSED int zend_jit_trap(dasm_State **Dst)
3038{
3039	|	int3
3040	return 1;
3041}
3042
3043static int zend_jit_align_func(dasm_State **Dst)
3044{
3045	reuse_ip = 0;
3046	delayed_call_chain = 0;
3047	last_valid_opline = NULL;
3048	use_last_vald_opline = 0;
3049	track_last_valid_opline = 0;
3050	jit_return_label = -1;
3051	|.align 16
3052	return 1;
3053}
3054
3055static int zend_jit_prologue(dasm_State **Dst)
3056{
3057	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3058		|	SUB_HYBRID_SPAD
3059	} else if (GCC_GLOBAL_REGS) {
3060		|	sub r4, SPAD // stack alignment
3061	} else {
3062		|	sub r4, NR_SPAD // stack alignment
3063		|	mov aword T2, FP // save FP
3064		|	mov aword T3, RX // save IP
3065		|	mov FP, FCARG1a
3066	}
3067	return 1;
3068}
3069
3070static int zend_jit_label(dasm_State **Dst, unsigned int label)
3071{
3072	|=>label:
3073	return 1;
3074}
3075
3076static int zend_jit_save_call_chain(dasm_State **Dst, uint32_t call_level)
3077{
3078	|	// call->prev_execute_data = EX(call);
3079	if (call_level == 1) {
3080		|	mov aword EX:RX->prev_execute_data, 0
3081	} else {
3082		|	mov r0, EX->call
3083		|	mov EX:RX->prev_execute_data, r0
3084	}
3085	|	// EX(call) = call;
3086	|	mov EX->call, RX
3087
3088	delayed_call_chain = 0;
3089
3090	return 1;
3091}
3092
3093static int zend_jit_set_ip(dasm_State **Dst, const zend_op *opline)
3094{
3095	if (last_valid_opline == opline) {
3096		zend_jit_use_last_valid_opline();
3097	} else if (GCC_GLOBAL_REGS && last_valid_opline) {
3098		zend_jit_use_last_valid_opline();
3099		|	ADD_IP (opline - last_valid_opline) * sizeof(zend_op);
3100	} else {
3101		|	LOAD_IP_ADDR opline
3102	}
3103	zend_jit_set_last_valid_opline(opline);
3104
3105	return 1;
3106}
3107
3108static int zend_jit_set_ip_ex(dasm_State **Dst, const zend_op *opline, bool set_ip_reg)
3109{
3110	if (last_valid_opline == opline) {
3111		zend_jit_use_last_valid_opline();
3112	} else if (GCC_GLOBAL_REGS && last_valid_opline) {
3113		zend_jit_use_last_valid_opline();
3114		|	ADD_IP (opline - last_valid_opline) * sizeof(zend_op);
3115	} else if (!GCC_GLOBAL_REGS && set_ip_reg) {
3116		|	LOAD_ADDR RX, opline
3117		|	mov aword EX->opline, RX
3118	} else {
3119		|	LOAD_IP_ADDR opline
3120	}
3121	zend_jit_set_last_valid_opline(opline);
3122
3123	return 1;
3124}
3125
3126static int zend_jit_set_valid_ip(dasm_State **Dst, const zend_op *opline)
3127{
3128	if (delayed_call_chain) {
3129		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
3130			return 0;
3131		}
3132	}
3133	if (!zend_jit_set_ip(Dst, opline)) {
3134		return 0;
3135	}
3136	reuse_ip = 0;
3137	return 1;
3138}
3139
3140static int zend_jit_check_timeout(dasm_State **Dst, const zend_op *opline, const void *exit_addr)
3141{
3142#if 0
3143	if (!zend_jit_set_valid_ip(Dst, opline)) {
3144		return 0;
3145	}
3146	|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3147	|	jne ->interrupt_handler
3148#else
3149	|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3150	if (exit_addr) {
3151		|	jne &exit_addr
3152	} else if (last_valid_opline == opline) {
3153		||		zend_jit_use_last_valid_opline();
3154		|	jne ->interrupt_handler
3155	} else {
3156		|	jne >1
3157		|.cold_code
3158		|1:
3159		|	LOAD_IP_ADDR opline
3160		|	jmp ->interrupt_handler
3161		|.code
3162	}
3163#endif
3164	return 1;
3165}
3166
3167static int zend_jit_trace_end_loop(dasm_State **Dst, int loop_label, const void *timeout_exit_addr)
3168{
3169	if (timeout_exit_addr) {
3170		|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3171		|	je =>loop_label
3172		|	jmp &timeout_exit_addr
3173	} else {
3174		|	jmp =>loop_label
3175	}
3176	return 1;
3177}
3178
3179static int zend_jit_check_exception(dasm_State **Dst)
3180{
3181	|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
3182	|	jne ->exception_handler
3183	return 1;
3184}
3185
3186static int zend_jit_check_exception_undef_result(dasm_State **Dst, const zend_op *opline)
3187{
3188	if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
3189		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
3190		|	jne ->exception_handler_undef
3191		return 1;
3192	}
3193	return zend_jit_check_exception(Dst);
3194}
3195
3196static int zend_jit_trace_begin(dasm_State **Dst, uint32_t trace_num, zend_jit_trace_info *parent, uint32_t exit_num)
3197{
3198	zend_regset regset = ZEND_REGSET_SCRATCH;
3199
3200#if ZTS
3201	if (1) {
3202#else
3203	if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(jit_trace_num)))) {
3204#endif
3205		/* assignment to EG(jit_trace_num) shouldn't clober CPU register used by deoptimizer */
3206		if (parent) {
3207			int i;
3208			int parent_vars_count = parent->exit_info[exit_num].stack_size;
3209			zend_jit_trace_stack *parent_stack =
3210				parent->stack_map +
3211				parent->exit_info[exit_num].stack_offset;
3212
3213			for (i = 0; i < parent_vars_count; i++) {
3214				if (STACK_REG(parent_stack, i) != ZREG_NONE) {
3215					if (STACK_REG(parent_stack, i) < ZREG_NUM) {
3216						ZEND_REGSET_EXCL(regset, STACK_REG(parent_stack, i));
3217					} else if (STACK_REG(parent_stack, i) == ZREG_ZVAL_COPY_GPR0) {
3218						ZEND_REGSET_EXCL(regset, ZREG_R0);
3219					}
3220				}
3221			}
3222		}
3223	}
3224
3225	if (parent && parent->exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL) {
3226		ZEND_REGSET_EXCL(regset, ZREG_R0);
3227	}
3228
3229	current_trace_num = trace_num;
3230
3231	|	// EG(jit_trace_num) = trace_num;
3232	if (regset == ZEND_REGSET_EMPTY) {
3233		|	push r0
3234		|	MEM_STORE_ZTS dword, executor_globals, jit_trace_num, trace_num, r0
3235		|	pop r0
3236	} else {
3237		zend_reg tmp = ZEND_REGSET_FIRST(regset);
3238
3239		|	MEM_STORE_ZTS dword, executor_globals, jit_trace_num, trace_num, Ra(tmp)
3240		(void)tmp;
3241	}
3242
3243	return 1;
3244}
3245
3246static int zend_jit_trace_end(dasm_State **Dst, zend_jit_trace_info *t)
3247{
3248	|.cold_code
3249	|=>1: // end of the code
3250	|.code
3251	return 1;
3252}
3253
3254/* This taken from LuaJIT. Thanks to Mike Pall. */
3255static uint32_t _asm_x86_inslen(const uint8_t* p)
3256{
3257	static const uint8_t map_op1[256] = {
3258		0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x20,
3259		0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x51,0x51,
3260		0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,
3261		0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,0x92,0x92,0x92,0x92,0x52,0x45,0x10,0x51,
3262#if defined(__x86_64__) || defined(_M_X64)
3263		0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,
3264#else
3265		0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,
3266#endif
3267		0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,
3268		0x51,0x51,0x92,0x92,0x10,0x10,0x12,0x11,0x45,0x86,0x52,0x93,0x51,0x51,0x51,0x51,
3269		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,
3270		0x93,0x86,0x93,0x93,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,
3271		0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x47,0x51,0x51,0x51,0x51,0x51,
3272#if defined(__x86_64__) || defined(_M_X64)
3273		0x59,0x59,0x59,0x59,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51,
3274#else
3275		0x55,0x55,0x55,0x55,0x51,0x51,0x51,0x51,0x52,0x45,0x51,0x51,0x51,0x51,0x51,0x51,
3276#endif
3277		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,
3278		0x93,0x93,0x53,0x51,0x70,0x71,0x93,0x86,0x54,0x51,0x53,0x51,0x51,0x52,0x51,0x51,
3279		0x92,0x92,0x92,0x92,0x52,0x52,0x51,0x51,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,
3280		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x45,0x45,0x47,0x52,0x51,0x51,0x51,0x51,
3281		0x10,0x51,0x10,0x10,0x51,0x51,0x63,0x66,0x51,0x51,0x51,0x51,0x51,0x51,0x92,0x92
3282	};
3283	static const uint8_t map_op2[256] = {
3284		0x93,0x93,0x93,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x51,0x52,0x51,0x93,0x52,0x94,
3285		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3286		0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3287		0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x34,0x51,0x35,0x51,0x51,0x51,0x51,0x51,
3288		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3289		0x53,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3290		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3291		0x94,0x54,0x54,0x54,0x93,0x93,0x93,0x52,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3292		0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,
3293		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3294		0x52,0x52,0x52,0x93,0x94,0x93,0x51,0x51,0x52,0x52,0x52,0x93,0x94,0x93,0x93,0x93,
3295		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x94,0x93,0x93,0x93,0x93,0x93,
3296		0x93,0x93,0x94,0x93,0x94,0x94,0x94,0x93,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,
3297		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3298		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,
3299		0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x52
3300	};
3301	uint32_t result = 0;
3302	uint32_t prefixes = 0;
3303	uint32_t x = map_op1[*p];
3304
3305	for (;;) {
3306		switch (x >> 4) {
3307			case 0:
3308				return result + x + (prefixes & 4);
3309			case 1:
3310				prefixes |= x;
3311				x = map_op1[*++p];
3312				result++;
3313				break;
3314			case 2:
3315				x = map_op2[*++p];
3316				break;
3317			case 3:
3318				p++;
3319				goto mrm;
3320			case 4:
3321				result -= (prefixes & 2);
3322				/* fallthrough */
3323			case 5:
3324				return result + (x & 15);
3325			case 6: /* Group 3. */
3326				if (p[1] & 0x38) {
3327					x = 2;
3328				} else if ((prefixes & 2) && (x == 0x66)) {
3329					x = 4;
3330				}
3331				goto mrm;
3332			case 7: /* VEX c4/c5. */
3333#if !defined(__x86_64__) && !defined(_M_X64)
3334				if (p[1] < 0xc0) {
3335					x = 2;
3336					goto mrm;
3337				}
3338#endif
3339				if (x == 0x70) {
3340					x = *++p & 0x1f;
3341					result++;
3342					if (x >= 2) {
3343						p += 2;
3344						result += 2;
3345						goto mrm;
3346					}
3347				}
3348				p++;
3349				result++;
3350				x = map_op2[*++p];
3351				break;
3352			case 8:
3353				result -= (prefixes & 2);
3354				/* fallthrough */
3355			case 9:
3356mrm:
3357				/* ModR/M and possibly SIB. */
3358				result += (x & 15);
3359				x = *++p;
3360				switch (x >> 6) {
3361					case 0:
3362						if ((x & 7) == 5) {
3363							return result + 4;
3364						}
3365						break;
3366					case 1:
3367						result++;
3368						break;
3369					case 2:
3370						result += 4;
3371						break;
3372					case 3:
3373						return result;
3374				}
3375				if ((x & 7) == 4) {
3376					result++;
3377					if (x < 0x40 && (p[1] & 7) == 5) {
3378						result += 4;
3379					}
3380				}
3381				return result;
3382		}
3383	}
3384}
3385
3386typedef ZEND_SET_ALIGNED(1, uint16_t unaligned_uint16_t);
3387typedef ZEND_SET_ALIGNED(1, int32_t unaligned_int32_t);
3388
3389static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size, const void *from_addr, const void *to_addr)
3390{
3391	int ret = 0;
3392	uint8_t *p, *end;
3393
3394	if (jmp_table_size) {
3395		const void **jmp_slot = (const void **)((char*)code + ZEND_MM_ALIGNED_SIZE_EX(size, sizeof(void*)));
3396
3397		do {
3398			if (*jmp_slot == from_addr) {
3399				*jmp_slot = to_addr;
3400				ret++;
3401			}
3402			jmp_slot++;
3403		} while (--jmp_table_size);
3404	}
3405
3406	p = (uint8_t*)code;
3407	end = p + size - 5;
3408	while (p < end) {
3409		if ((*(unaligned_uint16_t*)p & 0xf0ff) == 0x800f && p + *(unaligned_int32_t*)(p+2) == (uint8_t*)from_addr - 6) {
3410			*(unaligned_int32_t*)(p+2) = ((uint8_t*)to_addr - (p + 6));
3411			ret++;
3412		} else if (*p == 0xe9 && p + *(unaligned_int32_t*)(p+1) == (uint8_t*)from_addr - 5) {
3413			*(unaligned_int32_t*)(p+1) = ((uint8_t*)to_addr - (p + 5));
3414			ret++;
3415		}
3416		p += _asm_x86_inslen(p);
3417	}
3418#ifdef HAVE_VALGRIND
3419	VALGRIND_DISCARD_TRANSLATIONS(code, size);
3420#endif
3421	return ret;
3422}
3423
3424static int zend_jit_link_side_trace(const void *code, size_t size, uint32_t jmp_table_size, uint32_t exit_num, const void *addr)
3425{
3426	return zend_jit_patch(code, size, jmp_table_size, zend_jit_trace_get_exit_addr(exit_num), addr);
3427}
3428
3429static int zend_jit_trace_link_to_root(dasm_State **Dst, zend_jit_trace_info *t, const void *timeout_exit_addr)
3430{
3431	const void *link_addr;
3432	size_t prologue_size;
3433
3434	/* Skip prologue. */
3435	// TODO: don't hardcode this ???
3436	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3437#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
3438		prologue_size = 0;
3439#elif defined(__x86_64__) || defined(_M_X64)
3440		// sub r4, HYBRID_SPAD
3441		prologue_size = 4;
3442#else
3443		// sub r4, HYBRID_SPAD
3444		prologue_size = 3;
3445#endif
3446	} else if (GCC_GLOBAL_REGS) {
3447		// sub r4, SPAD // stack alignment
3448#if defined(__x86_64__) || defined(_M_X64)
3449		prologue_size = 4;
3450#else
3451		prologue_size = 3;
3452#endif
3453	} else {
3454		// sub r4, NR_SPAD // stack alignment
3455		// mov aword T2, FP // save FP
3456		// mov aword T3, RX // save IP
3457		// mov FP, FCARG1a
3458#if defined(__x86_64__) || defined(_M_X64)
3459		prologue_size = 17;
3460#else
3461		prologue_size = 13;
3462#endif
3463	}
3464	link_addr = (const void*)((const char*)t->code_start + prologue_size);
3465
3466	if (timeout_exit_addr) {
3467		/* Check timeout for links to LOOP */
3468		|	MEM_CMP_ZTS byte, executor_globals, vm_interrupt, 0, r0
3469		|	je &link_addr
3470		|	jmp &timeout_exit_addr
3471	} else {
3472		|	jmp &link_addr
3473	}
3474	return 1;
3475}
3476
3477static int zend_jit_trace_return(dasm_State **Dst, bool original_handler, const zend_op *opline)
3478{
3479#if 0
3480	|	jmp ->trace_escape
3481#else
3482	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3483		|	ADD_HYBRID_SPAD
3484		if (!original_handler) {
3485			|	JMP_IP
3486		} else {
3487			|	mov r0, EX->func
3488			|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
3489			|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
3490			|	jmp aword [IP + r0]
3491		}
3492	} else if (GCC_GLOBAL_REGS) {
3493		|	add r4, SPAD // stack alignment
3494		if (!original_handler) {
3495			|	JMP_IP
3496		} else {
3497			|	mov r0, EX->func
3498			|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
3499			|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
3500			|	jmp aword [IP + r0]
3501		}
3502	} else {
3503		if (original_handler) {
3504			|	mov FCARG1a, FP
3505			|	mov r0, EX->func
3506			|	mov r0, aword [r0 + offsetof(zend_op_array, reserved[zend_func_info_rid])]
3507			|	mov r0, aword [r0 + offsetof(zend_jit_op_array_trace_extension, offset)]
3508			|	call aword [IP + r0]
3509		}
3510		|	mov FP, aword T2 // restore FP
3511		|	mov RX, aword T3 // restore IP
3512		|	add r4, NR_SPAD // stack alignment
3513		if (!original_handler || !opline ||
3514		    (opline->opcode != ZEND_RETURN
3515		  && opline->opcode != ZEND_RETURN_BY_REF
3516		  && opline->opcode != ZEND_GENERATOR_RETURN
3517		  && opline->opcode != ZEND_GENERATOR_CREATE
3518		  && opline->opcode != ZEND_YIELD
3519		  && opline->opcode != ZEND_YIELD_FROM)) {
3520			|	mov r0, 2 // ZEND_VM_LEAVE
3521		}
3522		|	ret
3523	}
3524#endif
3525	return 1;
3526}
3527
3528static int zend_jit_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint8_t type)
3529{
3530	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
3531	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3532
3533	if (!exit_addr) {
3534		return 0;
3535	}
3536	|	IF_NOT_Z_TYPE FP + var, type, &exit_addr
3537
3538	return 1;
3539}
3540
3541static int zend_jit_scalar_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var)
3542{
3543	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
3544	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3545
3546	if (!exit_addr) {
3547		return 0;
3548	}
3549	|	cmp byte [FP+var+offsetof(zval, u1.v.type)], IS_STRING
3550	|	jae &exit_addr
3551
3552	return 1;
3553}
3554
3555static int zend_jit_packed_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint32_t op_info)
3556{
3557	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
3558	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3559
3560	if (!exit_addr) {
3561		return 0;
3562	}
3563
3564	|	GET_ZVAL_LVAL ZREG_FCARG1, ZEND_ADDR_MEM_ZVAL(ZREG_FP, var)
3565	if (op_info & MAY_BE_ARRAY_PACKED) {
3566		|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
3567		|	jz &exit_addr
3568	} else {
3569		|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
3570		|	jnz &exit_addr
3571	}
3572
3573	return 1;
3574}
3575
3576static 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)
3577{
3578	zend_jit_op_array_trace_extension *jit_extension =
3579		(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
3580	size_t offset = jit_extension->offset;
3581	const void *handler =
3582		(zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler;
3583
3584	if (!zend_jit_set_valid_ip(Dst, opline)) {
3585		return 0;
3586	}
3587	if (!GCC_GLOBAL_REGS) {
3588		|	mov FCARG1a, FP
3589	}
3590	|	EXT_CALL handler, r0
3591	if (may_throw
3592	 && opline->opcode != ZEND_RETURN
3593	 && opline->opcode != ZEND_RETURN_BY_REF) {
3594		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r1
3595		|	jne ->exception_handler
3596	}
3597
3598	while (trace->op != ZEND_JIT_TRACE_VM && trace->op != ZEND_JIT_TRACE_END) {
3599		trace++;
3600	}
3601
3602	if (!GCC_GLOBAL_REGS
3603	 && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_RETURN)) {
3604		if (opline->opcode == ZEND_RETURN ||
3605		    opline->opcode == ZEND_RETURN_BY_REF ||
3606		    opline->opcode == ZEND_DO_UCALL ||
3607		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
3608		    opline->opcode == ZEND_DO_FCALL ||
3609		    opline->opcode == ZEND_GENERATOR_CREATE) {
3610			|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r1
3611		}
3612	}
3613
3614	if (zend_jit_trace_may_exit(op_array, opline)) {
3615		if (opline->opcode == ZEND_RETURN ||
3616		    opline->opcode == ZEND_RETURN_BY_REF ||
3617		    opline->opcode == ZEND_GENERATOR_CREATE) {
3618
3619			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3620				if (trace->op != ZEND_JIT_TRACE_END ||
3621				    (trace->stop != ZEND_JIT_TRACE_STOP_RETURN &&
3622				     trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
3623					/* this check may be handled by the following OPLINE guard or jmp [IP] */
3624					|	cmp IP, zend_jit_halt_op
3625					|	je ->trace_halt
3626				}
3627			} else if (GCC_GLOBAL_REGS) {
3628				|	test IP, IP
3629				|	je ->trace_halt
3630			} else {
3631				|	test eax, eax
3632				|	jl ->trace_halt
3633			}
3634		} else if (opline->opcode == ZEND_EXIT ||
3635		           opline->opcode == ZEND_GENERATOR_RETURN ||
3636		           opline->opcode == ZEND_YIELD ||
3637		           opline->opcode == ZEND_YIELD_FROM) {
3638			|	jmp ->trace_halt
3639		}
3640		if (trace->op != ZEND_JIT_TRACE_END ||
3641		    (trace->stop != ZEND_JIT_TRACE_STOP_RETURN &&
3642		     trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
3643
3644			const zend_op *next_opline = trace->opline;
3645			const zend_op *exit_opline = NULL;
3646			uint32_t exit_point;
3647			const void *exit_addr;
3648			uint32_t old_info = 0;
3649			uint32_t old_res_info = 0;
3650			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
3651
3652			if (zend_is_smart_branch(opline)) {
3653				bool exit_if_true = 0;
3654				exit_opline = zend_jit_trace_get_exit_opline(trace, opline + 1, &exit_if_true);
3655			} else {
3656				switch (opline->opcode) {
3657					case ZEND_JMPZ:
3658					case ZEND_JMPNZ:
3659					case ZEND_JMPZ_EX:
3660					case ZEND_JMPNZ_EX:
3661					case ZEND_JMP_SET:
3662					case ZEND_COALESCE:
3663					case ZEND_JMP_NULL:
3664					case ZEND_FE_RESET_R:
3665					case ZEND_FE_RESET_RW:
3666						exit_opline = (trace->opline == opline + 1) ?
3667							OP_JMP_ADDR(opline, opline->op2) :
3668							opline + 1;
3669						break;
3670					case ZEND_JMPZNZ:
3671						exit_opline = (trace->opline == OP_JMP_ADDR(opline, opline->op2)) ?
3672							ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
3673							OP_JMP_ADDR(opline, opline->op2);
3674						break;
3675					case ZEND_FE_FETCH_R:
3676					case ZEND_FE_FETCH_RW:
3677						exit_opline = (trace->opline == opline + 1) ?
3678							ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
3679							opline + 1;
3680						break;
3681
3682				}
3683			}
3684
3685			switch (opline->opcode) {
3686				case ZEND_FE_FETCH_R:
3687				case ZEND_FE_FETCH_RW:
3688					if (opline->op2_type != IS_UNUSED) {
3689						old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var));
3690						SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1);
3691					}
3692					break;
3693			}
3694
3695			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
3696				old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
3697				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
3698			}
3699			exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
3700			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3701
3702			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
3703				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
3704			}
3705			switch (opline->opcode) {
3706				case ZEND_FE_FETCH_R:
3707				case ZEND_FE_FETCH_RW:
3708					if (opline->op2_type != IS_UNUSED) {
3709						SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var), old_info);
3710					}
3711					break;
3712			}
3713
3714			if (!exit_addr) {
3715				return 0;
3716			}
3717			|	CMP_IP next_opline
3718			|	jne &exit_addr
3719		}
3720	}
3721
3722	zend_jit_set_last_valid_opline(trace->opline);
3723
3724	return 1;
3725}
3726
3727static int zend_jit_handler(dasm_State **Dst, const zend_op *opline, int may_throw)
3728{
3729	const void *handler;
3730
3731	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3732		handler = zend_get_opcode_handler_func(opline);
3733	} else {
3734		handler = opline->handler;
3735	}
3736
3737	if (!zend_jit_set_valid_ip(Dst, opline)) {
3738		return 0;
3739	}
3740	if (!GCC_GLOBAL_REGS) {
3741		|	mov FCARG1a, FP
3742	}
3743	|	EXT_CALL handler, r0
3744	if (may_throw) {
3745		zend_jit_check_exception(Dst);
3746	}
3747
3748	/* Skip the following OP_DATA */
3749	switch (opline->opcode) {
3750		case ZEND_ASSIGN_DIM:
3751		case ZEND_ASSIGN_OBJ:
3752		case ZEND_ASSIGN_STATIC_PROP:
3753		case ZEND_ASSIGN_DIM_OP:
3754		case ZEND_ASSIGN_OBJ_OP:
3755		case ZEND_ASSIGN_STATIC_PROP_OP:
3756		case ZEND_ASSIGN_STATIC_PROP_REF:
3757		case ZEND_ASSIGN_OBJ_REF:
3758			zend_jit_set_last_valid_opline(opline + 2);
3759			break;
3760		default:
3761			zend_jit_set_last_valid_opline(opline + 1);
3762			break;
3763	}
3764
3765	return 1;
3766}
3767
3768static int zend_jit_tail_handler(dasm_State **Dst, const zend_op *opline)
3769{
3770	if (!zend_jit_set_valid_ip(Dst, opline)) {
3771		return 0;
3772	}
3773	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3774		if (opline->opcode == ZEND_DO_UCALL ||
3775		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
3776		    opline->opcode == ZEND_DO_FCALL ||
3777		    opline->opcode == ZEND_RETURN) {
3778
3779			/* Use inlined HYBRID VM handler */
3780			const void *handler = opline->handler;
3781
3782			|	ADD_HYBRID_SPAD
3783			|	EXT_JMP handler, r0
3784		} else {
3785			const void *handler = zend_get_opcode_handler_func(opline);
3786
3787			|	EXT_CALL handler, r0
3788			|	ADD_HYBRID_SPAD
3789			|	JMP_IP
3790		}
3791	} else {
3792		const void *handler = opline->handler;
3793
3794		if (GCC_GLOBAL_REGS) {
3795			|	add r4, SPAD // stack alignment
3796		} else {
3797			|	mov FCARG1a, FP
3798			|	mov FP, aword T2 // restore FP
3799			|	mov RX, aword T3 // restore IP
3800			|	add r4, NR_SPAD // stack alignment
3801		}
3802		|	EXT_JMP handler, r0
3803	}
3804	zend_jit_reset_last_valid_opline();
3805	return 1;
3806}
3807
3808static int zend_jit_trace_opline_guard(dasm_State **Dst, const zend_op *opline)
3809{
3810	uint32_t exit_point = zend_jit_trace_get_exit_point(NULL, 0);
3811	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3812
3813	if (!exit_addr) {
3814		return 0;
3815	}
3816	|	CMP_IP opline
3817	|	jne &exit_addr
3818
3819	zend_jit_set_last_valid_opline(opline);
3820
3821	return 1;
3822}
3823
3824static int zend_jit_jmp(dasm_State **Dst, unsigned int target_label)
3825{
3826	|	jmp =>target_label
3827	return 1;
3828}
3829
3830static int zend_jit_cond_jmp(dasm_State **Dst, const zend_op *next_opline, unsigned int target_label)
3831{
3832	|	CMP_IP next_opline
3833	|	jne =>target_label
3834
3835	zend_jit_set_last_valid_opline(next_opline);
3836
3837	return 1;
3838}
3839
3840#ifdef CONTEXT_THREADED_JIT
3841static int zend_jit_context_threaded_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
3842{
3843	if (!zend_jit_handler(Dst, opline, 1)) return 0;
3844	if (opline->opcode == ZEND_DO_UCALL) {
3845		|	call ->context_threaded_call
3846	} else {
3847		const zend_op *next_opline = opline + 1;
3848
3849		|	CMP_IP next_opline
3850		|	je =>next_block
3851		|	call ->context_threaded_call
3852	}
3853	return 1;
3854}
3855#endif
3856
3857static int zend_jit_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
3858{
3859#ifdef CONTEXT_THREADED_JIT
3860	return zend_jit_context_threaded_call(Dst, opline, next_block);
3861#else
3862	return zend_jit_tail_handler(Dst, opline);
3863#endif
3864}
3865
3866static int zend_jit_spill_store(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info, bool set_type)
3867{
3868	ZEND_ASSERT(Z_MODE(src) == IS_REG);
3869	ZEND_ASSERT(Z_MODE(dst) == IS_MEM_ZVAL);
3870
3871	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3872		|	SET_ZVAL_LVAL dst, Ra(Z_REG(src))
3873		if (set_type &&
3874		    (Z_REG(dst) != ZREG_FP ||
3875		     !JIT_G(current_frame) ||
3876		     STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_LONG)) {
3877			|	SET_ZVAL_TYPE_INFO dst, IS_LONG
3878		}
3879	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3880		|	DOUBLE_SET_ZVAL_DVAL dst, Z_REG(src)
3881		if (set_type &&
3882		    (Z_REG(dst) != ZREG_FP ||
3883		     !JIT_G(current_frame) ||
3884		     STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_DOUBLE)) {
3885			|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
3886		}
3887	} else {
3888		ZEND_UNREACHABLE();
3889	}
3890	return 1;
3891}
3892
3893static int zend_jit_load_reg(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
3894{
3895	ZEND_ASSERT(Z_MODE(src) == IS_MEM_ZVAL);
3896	ZEND_ASSERT(Z_MODE(dst) == IS_REG);
3897
3898	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3899		|	GET_ZVAL_LVAL Z_REG(dst), src
3900	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3901		|	DOUBLE_GET_ZVAL_DVAL Z_REG(dst), src
3902	} else {
3903		ZEND_UNREACHABLE();
3904	}
3905	return 1;
3906}
3907
3908static int zend_jit_store_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg, bool set_type)
3909{
3910	zend_jit_addr src = ZEND_ADDR_REG(reg);
3911	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3912
3913	return zend_jit_spill_store(Dst, src, dst, info, set_type);
3914}
3915
3916static int zend_jit_store_var_type(dasm_State **Dst, int var, uint32_t type)
3917{
3918	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3919
3920	|	SET_ZVAL_TYPE_INFO dst, type
3921	return 1;
3922}
3923
3924static int zend_jit_store_var_if_necessary(dasm_State **Dst, int var, zend_jit_addr src, uint32_t info)
3925{
3926	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
3927		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3928		return zend_jit_spill_store(Dst, src, dst, info, 1);
3929	}
3930	return 1;
3931}
3932
3933static 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)
3934{
3935	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
3936		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3937		bool set_type = 1;
3938
3939		if ((info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) ==
3940		    (old_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF))) {
3941			if (Z_MODE(old) != IS_REG || Z_LOAD(old) || Z_STORE(old)) {
3942				set_type = 0;
3943			}
3944		}
3945		return zend_jit_spill_store(Dst, src, dst, info, set_type);
3946	}
3947	return 1;
3948}
3949
3950static int zend_jit_load_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg)
3951{
3952	zend_jit_addr src = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3953	zend_jit_addr dst = ZEND_ADDR_REG(reg);
3954
3955	return zend_jit_load_reg(Dst, src, dst, info);
3956}
3957
3958static int zend_jit_invalidate_var_if_necessary(dasm_State **Dst, zend_uchar op_type, zend_jit_addr addr, znode_op op)
3959{
3960	if ((op_type & (IS_TMP_VAR|IS_VAR)) && Z_MODE(addr) == IS_REG && !Z_LOAD(addr) && !Z_STORE(addr)) {
3961		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var);
3962		|	SET_ZVAL_TYPE_INFO dst, IS_UNDEF
3963	}
3964	return 1;
3965}
3966
3967static int zend_jit_update_regs(dasm_State **Dst, uint32_t var, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
3968{
3969	if (!zend_jit_same_addr(src, dst)) {
3970		if (Z_MODE(src) == IS_REG) {
3971			if (Z_MODE(dst) == IS_REG) {
3972				if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3973					|	mov Ra(Z_REG(dst)), Ra(Z_REG(src))
3974				} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3975					|	SSE_AVX_INS movaps, vmovaps, xmm(Z_REG(dst)-ZREG_XMM0), xmm(Z_REG(src)-ZREG_XMM0)
3976				} else {
3977					ZEND_UNREACHABLE();
3978				}
3979				if (!Z_LOAD(src) && !Z_STORE(src) && Z_STORE(dst)) {
3980					zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3981
3982					if (!zend_jit_spill_store(Dst, dst, var_addr, info,
3983							JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3984							JIT_G(current_frame) == NULL ||
3985							STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3986							(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3987					)) {
3988						return 0;
3989					}
3990				}
3991			} else if (Z_MODE(dst) == IS_MEM_ZVAL) {
3992				if (!Z_LOAD(src) && !Z_STORE(src)) {
3993					if (!zend_jit_spill_store(Dst, src, dst, info,
3994							JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3995							JIT_G(current_frame) == NULL ||
3996							STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3997							(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3998					)) {
3999						return 0;
4000					}
4001				}
4002			} else {
4003				ZEND_UNREACHABLE();
4004			}
4005		} else if (Z_MODE(src) == IS_MEM_ZVAL) {
4006			if (Z_MODE(dst) == IS_REG) {
4007				if (!zend_jit_load_reg(Dst, src, dst, info)) {
4008					return 0;
4009				}
4010			} else {
4011				ZEND_UNREACHABLE();
4012			}
4013		} else {
4014			ZEND_UNREACHABLE();
4015		}
4016	} else if (Z_MODE(dst) == IS_REG && Z_STORE(dst)) {
4017		dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
4018		if (!zend_jit_spill_store(Dst, src, dst, info,
4019				JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
4020				JIT_G(current_frame) == NULL ||
4021				STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
4022				(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
4023		)) {
4024			return 0;
4025		}
4026	}
4027	return 1;
4028}
4029
4030static int zend_jit_escape_if_undef_r0(dasm_State **Dst, int var, uint32_t flags, const zend_op *opline)
4031{
4032	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
4033
4034	|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >1
4035
4036	if (flags & ZEND_JIT_EXIT_RESTORE_CALL) {
4037		if (!zend_jit_save_call_chain(Dst, -1)) {
4038			return 0;
4039		}
4040	}
4041
4042	ZEND_ASSERT(opline);
4043
4044	if ((opline-1)->opcode != ZEND_FETCH_CONSTANT
4045	 && (opline-1)->opcode != ZEND_FETCH_LIST_R
4046	 && ((opline-1)->op1_type & (IS_VAR|IS_TMP_VAR))
4047	 && !(flags & ZEND_JIT_EXIT_FREE_OP1)) {
4048		val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline-1)->op1.var);
4049
4050		|	IF_NOT_ZVAL_REFCOUNTED val_addr, >2
4051		|	GET_ZVAL_PTR r0, val_addr
4052		|	GC_ADDREF r0
4053		|2:
4054	}
4055
4056	|	LOAD_IP_ADDR (opline - 1)
4057	|	jmp ->trace_escape
4058	|1:
4059
4060	return 1;
4061}
4062
4063static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
4064{
4065	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
4066
4067	if (reg == ZREG_LONG_MIN_MINUS_1) {
4068		|.if X64
4069			|	SET_ZVAL_LVAL dst, 0x00000000
4070			|	SET_ZVAL_W2 dst, 0xc3e00000
4071		|.else
4072			|	SET_ZVAL_LVAL dst, 0x00200000
4073			|	SET_ZVAL_W2 dst, 0xc1e00000
4074		|.endif
4075		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
4076	} else if (reg == ZREG_LONG_MIN) {
4077		|.if X64
4078			|	SET_ZVAL_LVAL dst, 0x00000000
4079			|	SET_ZVAL_W2 dst, 0x80000000
4080		|.else
4081			|	SET_ZVAL_LVAL dst, ZEND_LONG_MIN
4082		|.endif
4083		|	SET_ZVAL_TYPE_INFO dst, IS_LONG
4084	} else if (reg == ZREG_LONG_MAX) {
4085		|.if X64
4086			|	SET_ZVAL_LVAL dst, 0xffffffff
4087			|	SET_ZVAL_W2 dst, 0x7fffffff
4088		|.else
4089			|	SET_ZVAL_LVAL dst, ZEND_LONG_MAX
4090		|.endif
4091		|	SET_ZVAL_TYPE_INFO dst, IS_LONG
4092	} else if (reg == ZREG_LONG_MAX_PLUS_1) {
4093		|.if X64
4094			|	SET_ZVAL_LVAL dst, 0
4095			|	SET_ZVAL_W2 dst, 0x43e00000
4096		|.else
4097			|	SET_ZVAL_LVAL dst, 0
4098			|	SET_ZVAL_W2 dst, 0x41e00000
4099		|.endif
4100		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
4101	} else if (reg == ZREG_NULL) {
4102		|	SET_ZVAL_TYPE_INFO dst, IS_NULL
4103	} else if (reg == ZREG_ZVAL_TRY_ADDREF) {
4104		|	IF_NOT_ZVAL_REFCOUNTED dst, >1
4105		|	GET_ZVAL_PTR r1, dst
4106		|	GC_ADDREF r1
4107		|1:
4108	} else if (reg == ZREG_ZVAL_COPY_GPR0) {
4109		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
4110
4111		|	ZVAL_COPY_VALUE dst, -1, val_addr, -1, ZREG_R1, ZREG_R2
4112		|	TRY_ADDREF -1, ch, r2
4113	} else {
4114		ZEND_UNREACHABLE();
4115	}
4116	return 1;
4117}
4118
4119static int zend_jit_free_trampoline(dasm_State **Dst)
4120{
4121	|	/// if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
4122	|	test dword [r0 + offsetof(zend_function, common.fn_flags)], ZEND_ACC_CALL_VIA_TRAMPOLINE
4123	|	jz >1
4124	|	mov FCARG1a, r0
4125	|	EXT_CALL zend_jit_free_trampoline_helper, r0
4126	|1:
4127	return 1;
4128}
4129
4130static 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)
4131{
4132	if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_LONG)) {
4133		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2
4134	}
4135	if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
4136		|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4137	}
4138	if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, MAY_BE_LONG)) {
4139		return 0;
4140	}
4141	if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4142		|	LONG_OP_WITH_32BIT_CONST add, op1_def_addr, Z_L(1)
4143	} else {
4144		|	LONG_OP_WITH_32BIT_CONST sub, op1_def_addr, Z_L(1)
4145	}
4146
4147	if (may_overflow &&
4148	    (((op1_def_info & MAY_BE_GUARD) && (op1_def_info & MAY_BE_LONG)) ||
4149	     ((opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD) && (res_info & MAY_BE_LONG))))) {
4150		int32_t exit_point;
4151		const void *exit_addr;
4152		zend_jit_trace_stack *stack;
4153		uint32_t old_op1_info, old_res_info = 0;
4154
4155		stack = JIT_G(current_frame)->stack;
4156		old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
4157		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_DOUBLE, 0);
4158		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4159			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MAX_PLUS_1);
4160		} else {
4161			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MIN_MINUS_1);
4162		}
4163		if (opline->result_type != IS_UNUSED) {
4164			old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
4165			if (opline->opcode == ZEND_PRE_INC) {
4166				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
4167				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX_PLUS_1);
4168			} else if (opline->opcode == ZEND_PRE_DEC) {
4169				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
4170				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN_MINUS_1);
4171			} else if (opline->opcode == ZEND_POST_INC) {
4172				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
4173				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX);
4174			} else if (opline->opcode == ZEND_POST_DEC) {
4175				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
4176				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN);
4177			}
4178		}
4179
4180		exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
4181		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
4182		if (!exit_addr) {
4183			return 0;
4184		}
4185		|	jo &exit_addr
4186
4187		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4188		    opline->result_type != IS_UNUSED) {
4189			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4190		}
4191
4192		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
4193		if (opline->result_type != IS_UNUSED) {
4194			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
4195		}
4196	} else if (may_overflow) {
4197		|	jo >1
4198		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4199		    opline->result_type != IS_UNUSED) {
4200			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4201		}
4202		|.cold_code
4203		|1:
4204		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4205			|.if X64
4206				|	mov64 rax, 0x43e0000000000000
4207				|	SET_ZVAL_LVAL op1_def_addr, rax
4208			|.else
4209				|	SET_ZVAL_LVAL op1_def_addr, 0
4210				|	SET_ZVAL_W2 op1_def_addr, 0x41e00000
4211			|.endif
4212		} else {
4213			|.if X64
4214				|	mov64 rax, 0xc3e0000000000000
4215				|	SET_ZVAL_LVAL op1_def_addr, rax
4216			|.else
4217				|	SET_ZVAL_LVAL op1_def_addr, 0x00200000
4218				|	SET_ZVAL_W2 op1_def_addr, 0xc1e00000
4219			|.endif
4220		}
4221		if (Z_MODE(op1_def_addr) == IS_MEM_ZVAL) {
4222			|	SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE
4223		}
4224		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4225		    opline->result_type != IS_UNUSED) {
4226			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_DOUBLE, ZREG_R0, ZREG_R1
4227		}
4228		|	jmp >3
4229		|.code
4230	} else {
4231		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4232		    opline->result_type != IS_UNUSED) {
4233			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
4234		}
4235	}
4236	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
4237		|.cold_code
4238		|2:
4239		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4240			|	SET_EX_OPLINE opline, r0
4241			if (op1_info & MAY_BE_UNDEF) {
4242				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >2
4243				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
4244				|	mov FCARG1d, opline->op1.var
4245				|	EXT_CALL zend_jit_undefined_op_helper, r0
4246				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL
4247				op1_info |= MAY_BE_NULL;
4248			}
4249			|2:
4250			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
4251
4252			|	// ZVAL_DEREF(var_ptr);
4253			if (op1_info & MAY_BE_REF) {
4254				|	IF_NOT_Z_TYPE, FCARG1a, IS_REFERENCE, >2
4255				|	GET_Z_PTR FCARG1a, FCARG1a
4256				|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
4257				|	jz >1
4258				if (RETURN_VALUE_USED(opline)) {
4259					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
4260				} else {
4261					|	xor FCARG2a, FCARG2a
4262				}
4263				if (opline->opcode == ZEND_PRE_INC) {
4264					|	EXT_CALL zend_jit_pre_inc_typed_ref, r0
4265				} else if (opline->opcode == ZEND_PRE_DEC) {
4266					|	EXT_CALL zend_jit_pre_dec_typed_ref, r0
4267				} else if (opline->opcode == ZEND_POST_INC) {
4268					|	EXT_CALL zend_jit_post_inc_typed_ref, r0
4269				} else if (opline->opcode == ZEND_POST_DEC) {
4270					|	EXT_CALL zend_jit_post_dec_typed_ref, r0
4271				} else {
4272					ZEND_UNREACHABLE();
4273				}
4274				zend_jit_check_exception(Dst);
4275				|	jmp >3
4276				|1:
4277				|	lea FCARG1a, [FCARG1a + offsetof(zend_reference, val)]
4278				|2:
4279			}
4280
4281			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
4282				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
4283
4284				|	ZVAL_COPY_VALUE res_addr, res_use_info, val_addr, op1_info, ZREG_R0, ZREG_R2
4285				|	TRY_ADDREF op1_info, ah, r2
4286			}
4287			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4288				if (opline->opcode == ZEND_PRE_INC && opline->result_type != IS_UNUSED) {
4289					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
4290					|	EXT_CALL zend_jit_pre_inc, r0
4291				} else {
4292					|	EXT_CALL increment_function, r0
4293				}
4294			} else {
4295				if (opline->opcode == ZEND_PRE_DEC && opline->result_type != IS_UNUSED) {
4296					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
4297					|	EXT_CALL zend_jit_pre_dec, r0
4298				} else {
4299					|	EXT_CALL decrement_function, r0
4300				}
4301			}
4302			if (may_throw) {
4303				zend_jit_check_exception(Dst);
4304			}
4305		} else {
4306			zend_reg tmp_reg;
4307
4308			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
4309				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_DOUBLE, ZREG_R0, ZREG_R2
4310			}
4311			if (Z_MODE(op1_def_addr) == IS_REG) {
4312				tmp_reg = Z_REG(op1_def_addr);
4313			} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4314				tmp_reg = Z_REG(op1_addr);
4315			} else {
4316				tmp_reg = ZREG_XMM0;
4317			}
4318			|	DOUBLE_GET_ZVAL_DVAL tmp_reg, op1_addr
4319			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
4320				if (CAN_USE_AVX()) {
4321					|	vaddsd xmm(tmp_reg-ZREG_XMM0), xmm(tmp_reg-ZREG_XMM0), qword [->one]
4322				} else {
4323					|	addsd xmm(tmp_reg-ZREG_XMM0), qword [->one]
4324				}
4325			} else {
4326				if (CAN_USE_AVX()) {
4327					|	vsubsd xmm(tmp_reg-ZREG_XMM0), xmm(tmp_reg-ZREG_XMM0), qword [->one]
4328				} else {
4329					|	subsd xmm(tmp_reg-ZREG_XMM0), qword [->one]
4330				}
4331			}
4332			|	DOUBLE_SET_ZVAL_DVAL op1_def_addr, tmp_reg
4333			if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4334			    opline->result_type != IS_UNUSED) {
4335				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, op1_def_info, ZREG_R0, ZREG_R1
4336				|	TRY_ADDREF op1_def_info, ah, r1
4337			}
4338		}
4339		|	jmp >3
4340		|.code
4341	}
4342	|3:
4343	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_def_addr, op1_def_info, op1_addr, op1_info)) {
4344		return 0;
4345	}
4346	if (opline->result_type != IS_UNUSED) {
4347		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
4348			return 0;
4349		}
4350	}
4351	return 1;
4352}
4353
4354static int zend_jit_opline_uses_reg(const zend_op  *opline, int8_t reg)
4355{
4356	if ((opline+1)->opcode == ZEND_OP_DATA
4357	 && ((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV))
4358	 && JIT_G(current_frame)->stack[EX_VAR_TO_NUM((opline+1)->op1.var)].reg == reg) {
4359		return 1;
4360	}
4361	return
4362		((opline->result_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4363			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->result.var)].reg == reg) ||
4364		((opline->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4365			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op1.var)].reg == reg) ||
4366		((opline->op2_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4367			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op2.var)].reg == reg);
4368}
4369
4370static int zend_jit_math_long_long(dasm_State    **Dst,
4371                                   const zend_op  *opline,
4372                                   zend_uchar      opcode,
4373                                   zend_jit_addr   op1_addr,
4374                                   zend_jit_addr   op2_addr,
4375                                   zend_jit_addr   res_addr,
4376                                   uint32_t        res_info,
4377                                   uint32_t        res_use_info,
4378                                   int             may_overflow)
4379{
4380	bool must_set_cflags = 0;
4381	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4382	zend_reg result_reg;
4383	zend_reg tmp_reg = ZREG_R0;
4384
4385	if (Z_MODE(res_addr) == IS_REG && (res_info & MAY_BE_LONG)) {
4386		if (may_overflow && (res_info & MAY_BE_GUARD)
4387		 && JIT_G(current_frame)
4388		 && zend_jit_opline_uses_reg(opline, Z_REG(res_addr))) {
4389			result_reg = ZREG_R0;
4390		} else {
4391			result_reg = Z_REG(res_addr);
4392		}
4393	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr) && !may_overflow) {
4394		result_reg = Z_REG(op1_addr);
4395	} else if (Z_REG(res_addr) != ZREG_R0) {
4396		result_reg = ZREG_R0;
4397	} else {
4398		/* ASSIGN_DIM_OP */
4399		result_reg = ZREG_FCARG1;
4400		tmp_reg = ZREG_FCARG1;
4401	}
4402
4403	if (may_overflow) {
4404		must_set_cflags = 1;
4405	} else {
4406		const zend_op *next_opline = opline + 1;
4407
4408		if (next_opline->opcode == ZEND_IS_EQUAL ||
4409				next_opline->opcode == ZEND_IS_NOT_EQUAL ||
4410				next_opline->opcode == ZEND_IS_SMALLER ||
4411				next_opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
4412				next_opline->opcode == ZEND_CASE ||
4413				next_opline->opcode == ZEND_IS_IDENTICAL ||
4414				next_opline->opcode == ZEND_IS_NOT_IDENTICAL ||
4415				next_opline->opcode == ZEND_CASE_STRICT) {
4416			if (next_opline->op1_type == IS_CONST
4417			 && Z_TYPE_P(RT_CONSTANT(next_opline, next_opline->op1)) == IS_LONG
4418			 && Z_LVAL_P(RT_CONSTANT(next_opline, next_opline->op1)) == 0
4419			 && next_opline->op2_type == opline->result_type
4420			 && next_opline->op2.var == opline->result.var) {
4421				must_set_cflags = 1;
4422			} else if (next_opline->op2_type == IS_CONST
4423			 && Z_TYPE_P(RT_CONSTANT(next_opline, next_opline->op2)) == IS_LONG
4424			 && Z_LVAL_P(RT_CONSTANT(next_opline, next_opline->op2)) == 0
4425			 && next_opline->op2_type == opline->result_type
4426			 && next_opline->op2.var == opline->result.var) {
4427				must_set_cflags = 1;
4428			}
4429		}
4430	}
4431
4432	if (opcode == ZEND_MUL &&
4433			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4434			Z_LVAL_P(Z_ZV(op2_addr)) == 2) {
4435		if (Z_MODE(op1_addr) == IS_REG && !must_set_cflags) {
4436			|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))+Ra(Z_REG(op1_addr))]
4437		} else {
4438			|	GET_ZVAL_LVAL result_reg, op1_addr
4439			|	add Ra(result_reg), Ra(result_reg)
4440		}
4441	} else if (opcode == ZEND_MUL &&
4442			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4443			!must_set_cflags &&
4444			Z_LVAL_P(Z_ZV(op2_addr)) > 0 &&
4445			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr)))) {
4446		|	GET_ZVAL_LVAL result_reg, op1_addr
4447		|	shl Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
4448	} else if (opcode == ZEND_MUL &&
4449			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4450			Z_LVAL_P(Z_ZV(op1_addr)) == 2) {
4451		if (Z_MODE(op2_addr) == IS_REG && !must_set_cflags) {
4452			|	lea Ra(result_reg), [Ra(Z_REG(op2_addr))+Ra(Z_REG(op2_addr))]
4453		} else {
4454			|	GET_ZVAL_LVAL result_reg, op2_addr
4455			|	add Ra(result_reg), Ra(result_reg)
4456		}
4457	} else if (opcode == ZEND_MUL &&
4458			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4459			!must_set_cflags &&
4460			Z_LVAL_P(Z_ZV(op1_addr)) > 0 &&
4461			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op1_addr)))) {
4462		|	GET_ZVAL_LVAL result_reg, op2_addr
4463		|	shl Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
4464	} else if (opcode == ZEND_DIV &&
4465			(Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4466			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr))))) {
4467		|	GET_ZVAL_LVAL result_reg, op1_addr
4468		|	shr Ra(result_reg), zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
4469	} else if (opcode == ZEND_ADD &&
4470			!must_set_cflags &&
4471			Z_MODE(op1_addr) == IS_REG &&
4472			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4473			IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(op2_addr)))) {
4474		|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))+Z_LVAL_P(Z_ZV(op2_addr))]
4475	} else if (opcode == ZEND_ADD &&
4476			!must_set_cflags &&
4477			Z_MODE(op2_addr) == IS_REG &&
4478			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4479			IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(op1_addr)))) {
4480		|	lea Ra(result_reg), [Ra(Z_REG(op2_addr))+Z_LVAL_P(Z_ZV(op1_addr))]
4481	} else if (opcode == ZEND_SUB &&
4482			!must_set_cflags &&
4483			Z_MODE(op1_addr) == IS_REG &&
4484			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4485			IS_SIGNED_32BIT(-Z_LVAL_P(Z_ZV(op2_addr)))) {
4486		|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))-Z_LVAL_P(Z_ZV(op2_addr))]
4487	} else {
4488		|	GET_ZVAL_LVAL result_reg, op1_addr
4489		if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4490		 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4491		 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4492			/* +/- 0 */
4493			may_overflow = 0;
4494		} else if (same_ops && opcode != ZEND_DIV) {
4495			|	LONG_MATH_REG opcode, Ra(result_reg), Ra(result_reg)
4496		} else {
4497			zend_reg tmp_reg;
4498
4499			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4500				tmp_reg = ZREG_R1;
4501			} else if (result_reg != ZREG_R0) {
4502				tmp_reg = ZREG_R0;
4503			} else {
4504				tmp_reg = ZREG_R1;
4505			}
4506			|	LONG_MATH opcode, result_reg, op2_addr, tmp_reg
4507			(void)tmp_reg;
4508		}
4509	}
4510	if (may_overflow) {
4511		if (res_info & MAY_BE_GUARD) {
4512			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
4513			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
4514			if (!exit_addr) {
4515				return 0;
4516			}
4517			if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) {
4518				|	jo &exit_addr
4519				if (Z_MODE(res_addr) == IS_REG && result_reg != Z_REG(res_addr)) {
4520					|	mov Ra(Z_REG(res_addr)), Ra(result_reg)
4521				}
4522			} else if ((res_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
4523				|	jno &exit_addr
4524			} else {
4525				ZEND_UNREACHABLE();
4526			}
4527		} else {
4528			if (res_info & MAY_BE_LONG) {
4529				|	jo >1
4530			} else {
4531				|	jno >1
4532			}
4533		}
4534	}
4535
4536	if (Z_MODE(res_addr) == IS_MEM_ZVAL && (res_info & MAY_BE_LONG)) {
4537		|	SET_ZVAL_LVAL res_addr, Ra(result_reg)
4538		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4539			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
4540				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
4541			}
4542		}
4543	}
4544
4545	if (may_overflow && (!(res_info & MAY_BE_GUARD) || (res_info & MAY_BE_ANY) == MAY_BE_DOUBLE)) {
4546		zend_reg tmp_reg1 = ZREG_XMM0;
4547		zend_reg tmp_reg2 = ZREG_XMM1;
4548
4549		if (res_info & MAY_BE_LONG) {
4550			|.cold_code
4551			|1:
4552		}
4553
4554		do {
4555			if ((sizeof(void*) == 8 || Z_MODE(res_addr) != IS_REG) &&
4556			    ((Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 1) ||
4557			     (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 1))) {
4558				if (opcode == ZEND_ADD) {
4559					|.if X64
4560						|	mov64 Ra(tmp_reg), 0x43e0000000000000
4561						if (Z_MODE(res_addr) == IS_REG) {
4562							|	movd xmm(Z_REG(res_addr)-ZREG_XMM0), Ra(tmp_reg)
4563						} else {
4564							|	SET_ZVAL_LVAL res_addr, Ra(tmp_reg)
4565						}
4566					|.else
4567						|	SET_ZVAL_LVAL res_addr, 0
4568						|	SET_ZVAL_W2 res_addr, 0x41e00000
4569					|.endif
4570					break;
4571				} else if (opcode == ZEND_SUB) {
4572					|.if X64
4573						|	mov64 Ra(tmp_reg), 0xc3e0000000000000
4574						if (Z_MODE(res_addr) == IS_REG) {
4575							|	movd xmm(Z_REG(res_addr)-ZREG_XMM0), Ra(tmp_reg)
4576						} else {
4577							|	SET_ZVAL_LVAL res_addr, Ra(tmp_reg)
4578						}
4579					|.else
4580						|	SET_ZVAL_LVAL res_addr, 0x00200000
4581						|	SET_ZVAL_W2 res_addr, 0xc1e00000
4582					|.endif
4583					break;
4584				}
4585			}
4586
4587			|	DOUBLE_GET_ZVAL_LVAL tmp_reg1, op1_addr, tmp_reg
4588			|	DOUBLE_GET_ZVAL_LVAL tmp_reg2, op2_addr, tmp_reg
4589			if (CAN_USE_AVX()) {
4590				|	AVX_MATH_REG opcode, tmp_reg1, tmp_reg1, tmp_reg2
4591			} else {
4592				|	SSE_MATH_REG opcode, tmp_reg1, tmp_reg2
4593			}
4594			|	DOUBLE_SET_ZVAL_DVAL res_addr, tmp_reg1
4595		} while (0);
4596
4597		if (Z_MODE(res_addr) == IS_MEM_ZVAL
4598		 && (res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4599			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4600		}
4601		if (res_info & MAY_BE_LONG) {
4602			|	jmp >2
4603			|.code
4604		}
4605		|2:
4606	}
4607
4608	return 1;
4609}
4610
4611static int zend_jit_math_long_double(dasm_State    **Dst,
4612                                     zend_uchar      opcode,
4613                                     zend_jit_addr   op1_addr,
4614                                     zend_jit_addr   op2_addr,
4615                                     zend_jit_addr   res_addr,
4616                                     uint32_t        res_use_info)
4617{
4618	zend_reg result_reg =
4619		(Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : ZREG_XMM0;
4620	zend_reg tmp_reg;
4621
4622	if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4623		/* ASSIGN_DIM_OP */
4624		tmp_reg = ZREG_R1;
4625	} else {
4626		tmp_reg = ZREG_R0;
4627	}
4628
4629	|	DOUBLE_GET_ZVAL_LVAL result_reg, op1_addr, tmp_reg
4630
4631	if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4632		/* ASSIGN_DIM_OP */
4633		if (CAN_USE_AVX()) {
4634			|	AVX_MATH opcode, result_reg, result_reg, op2_addr, r1
4635		} else {
4636			|	SSE_MATH opcode, result_reg, op2_addr, r1
4637		}
4638	} else {
4639		if (CAN_USE_AVX()) {
4640			|	AVX_MATH opcode, result_reg, result_reg, op2_addr, r0
4641		} else {
4642			|	SSE_MATH opcode, result_reg, op2_addr, r0
4643		}
4644	}
4645	|	DOUBLE_SET_ZVAL_DVAL res_addr, result_reg
4646
4647	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4648		if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4649			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4650		}
4651	}
4652
4653	return 1;
4654}
4655
4656static int zend_jit_math_double_long(dasm_State    **Dst,
4657                                     zend_uchar      opcode,
4658                                     zend_jit_addr   op1_addr,
4659                                     zend_jit_addr   op2_addr,
4660                                     zend_jit_addr   res_addr,
4661                                     uint32_t        res_use_info)
4662{
4663	zend_reg result_reg, tmp_reg_gp;
4664
4665	if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4666		/* ASSIGN_DIM_OP */
4667		tmp_reg_gp = ZREG_R1;
4668	} else {
4669		tmp_reg_gp = ZREG_R0;
4670	}
4671
4672	if (zend_is_commutative(opcode)
4673	 && (Z_MODE(res_addr) != IS_REG || Z_MODE(op1_addr) != IS_REG || Z_REG(res_addr) != Z_REG(op1_addr))) {
4674		if (Z_MODE(res_addr) == IS_REG) {
4675			result_reg = Z_REG(res_addr);
4676		} else {
4677			result_reg = ZREG_XMM0;
4678		}
4679		|	DOUBLE_GET_ZVAL_LVAL result_reg, op2_addr, tmp_reg_gp
4680		if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4681			/* ASSIGN_DIM_OP */
4682			if (CAN_USE_AVX()) {
4683				|	AVX_MATH opcode, result_reg, result_reg, op1_addr, r1
4684			} else {
4685				|	SSE_MATH opcode, result_reg, op1_addr, r1
4686			}
4687		} else {
4688			if (CAN_USE_AVX()) {
4689				|	AVX_MATH opcode, result_reg, result_reg, op1_addr, r0
4690			} else {
4691				|	SSE_MATH opcode, result_reg, op1_addr, r0
4692			}
4693		}
4694	} else {
4695		zend_reg tmp_reg;
4696
4697		if (Z_MODE(res_addr) == IS_REG) {
4698			result_reg = Z_REG(res_addr);
4699			tmp_reg = (result_reg == ZREG_XMM0) ? ZREG_XMM1 : ZREG_XMM0;
4700		} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4701			result_reg = Z_REG(op1_addr);
4702			tmp_reg = ZREG_XMM0;
4703		} else {
4704			result_reg = ZREG_XMM0;
4705			tmp_reg = ZREG_XMM1;
4706		}
4707		if (CAN_USE_AVX()) {
4708			zend_reg op1_reg;
4709
4710			if (Z_MODE(op1_addr) == IS_REG) {
4711				op1_reg = Z_REG(op1_addr);
4712			} else {
4713				|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4714				op1_reg = result_reg;
4715			}
4716			if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4717			 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4718			 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4719				/* +/- 0 */
4720			} else {
4721				|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, tmp_reg_gp
4722				|	AVX_MATH_REG opcode, result_reg, op1_reg, tmp_reg
4723			}
4724		} else {
4725			|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4726			if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4727			 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4728			 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4729				/* +/- 0 */
4730			} else {
4731				|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, tmp_reg_gp
4732				|	SSE_MATH_REG opcode, result_reg, tmp_reg
4733			}
4734		}
4735	}
4736	|	DOUBLE_SET_ZVAL_DVAL res_addr, result_reg
4737
4738	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4739		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4740			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4741				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4742			}
4743		}
4744	}
4745
4746	return 1;
4747}
4748
4749static int zend_jit_math_double_double(dasm_State    **Dst,
4750                                       zend_uchar      opcode,
4751                                       zend_jit_addr   op1_addr,
4752                                       zend_jit_addr   op2_addr,
4753                                       zend_jit_addr   res_addr,
4754                                       uint32_t        res_use_info)
4755{
4756	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4757	zend_reg result_reg;
4758
4759	if (Z_MODE(res_addr) == IS_REG) {
4760		result_reg = Z_REG(res_addr);
4761	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4762		result_reg = Z_REG(op1_addr);
4763	} else if (zend_is_commutative(opcode) && Z_MODE(op2_addr) == IS_REG && Z_LAST_USE(op2_addr)) {
4764		result_reg = Z_REG(op2_addr);
4765	} else {
4766		result_reg = ZREG_XMM0;
4767	}
4768
4769	if (CAN_USE_AVX()) {
4770		zend_reg op1_reg;
4771		zend_jit_addr val_addr;
4772
4773		if (Z_MODE(op1_addr) == IS_REG) {
4774			op1_reg = Z_REG(op1_addr);
4775			val_addr = op2_addr;
4776		} else if (Z_MODE(op2_addr) == IS_REG && zend_is_commutative(opcode)) {
4777			op1_reg = Z_REG(op2_addr);
4778			val_addr = op1_addr;
4779		} else {
4780			|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4781			op1_reg = result_reg;
4782			val_addr = op2_addr;
4783		}
4784		if ((opcode == ZEND_MUL) &&
4785			Z_MODE(val_addr) == IS_CONST_ZVAL && Z_DVAL_P(Z_ZV(val_addr)) == 2.0) {
4786			|	AVX_MATH_REG ZEND_ADD, result_reg, op1_reg, op1_reg
4787		} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4788			/* ASSIGN_DIM_OP */
4789			|	AVX_MATH opcode, result_reg, op1_reg, val_addr, r1
4790		} else {
4791			|	AVX_MATH opcode, result_reg, op1_reg, val_addr, r0
4792		}
4793	} else {
4794		zend_jit_addr val_addr;
4795
4796		if (Z_MODE(op1_addr) != IS_REG && Z_MODE(op2_addr) == IS_REG && zend_is_commutative(opcode)) {
4797			|	DOUBLE_GET_ZVAL_DVAL result_reg, op2_addr
4798			val_addr = op1_addr;
4799		} else {
4800			|	DOUBLE_GET_ZVAL_DVAL result_reg, op1_addr
4801			val_addr = op2_addr;
4802		}
4803		if (same_ops) {
4804			|	SSE_MATH_REG opcode, result_reg, result_reg
4805		} else if ((opcode == ZEND_MUL) &&
4806			Z_MODE(val_addr) == IS_CONST_ZVAL && Z_DVAL_P(Z_ZV(val_addr)) == 2.0) {
4807			|	SSE_MATH_REG ZEND_ADD, result_reg, result_reg
4808		} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
4809			/* ASSIGN_DIM_OP */
4810			|	SSE_MATH opcode, result_reg, val_addr, r1
4811		} else {
4812			|	SSE_MATH opcode, result_reg, val_addr, r0
4813		}
4814	}
4815	|	DOUBLE_SET_ZVAL_DVAL res_addr, result_reg
4816
4817	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4818		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4819			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4820				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
4821			}
4822		}
4823	}
4824
4825	return 1;
4826}
4827
4828static int zend_jit_math_helper(dasm_State    **Dst,
4829                                const zend_op  *opline,
4830                                zend_uchar      opcode,
4831                                zend_uchar      op1_type,
4832                                znode_op        op1,
4833                                zend_jit_addr   op1_addr,
4834                                uint32_t        op1_info,
4835                                zend_uchar      op2_type,
4836                                znode_op        op2,
4837                                zend_jit_addr   op2_addr,
4838                                uint32_t        op2_info,
4839                                uint32_t        res_var,
4840                                zend_jit_addr   res_addr,
4841                                uint32_t        res_info,
4842                                uint32_t        res_use_info,
4843                                int             may_overflow,
4844                                int             may_throw)
4845/* Labels: 1,2,3,4,5,6 */
4846{
4847	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4848
4849	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
4850		if (op1_info & (MAY_BE_ANY-MAY_BE_LONG)) {
4851			if (op1_info & MAY_BE_DOUBLE) {
4852				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
4853			} else {
4854				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
4855			}
4856		}
4857		if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_LONG))) {
4858			if (op2_info & MAY_BE_DOUBLE) {
4859				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >1
4860				|.cold_code
4861				|1:
4862				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4863					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6
4864				}
4865				if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4866					return 0;
4867				}
4868				|	jmp >5
4869				|.code
4870			} else {
4871				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
4872			}
4873		}
4874		if (!zend_jit_math_long_long(Dst, opline, opcode, op1_addr, op2_addr, res_addr, res_info, res_use_info, may_overflow)) {
4875			return 0;
4876		}
4877		if (op1_info & MAY_BE_DOUBLE) {
4878			|.cold_code
4879			|3:
4880			if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4881				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6
4882			}
4883			if (op2_info & MAY_BE_DOUBLE) {
4884				if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4885					if (!same_ops) {
4886						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >1
4887					} else {
4888						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >6
4889					}
4890				}
4891				if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4892					return 0;
4893				}
4894				|	jmp >5
4895			}
4896			if (!same_ops) {
4897				|1:
4898				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4899					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
4900				}
4901				if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4902					return 0;
4903				}
4904				|	jmp >5
4905			}
4906			|.code
4907		}
4908	} else if ((op1_info & MAY_BE_DOUBLE) &&
4909	           !(op1_info & MAY_BE_LONG) &&
4910	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4911	           (res_info & MAY_BE_DOUBLE)) {
4912		if (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
4913			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6
4914		}
4915		if (op2_info & MAY_BE_DOUBLE) {
4916			if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4917				if (!same_ops && (op2_info & MAY_BE_LONG)) {
4918					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >1
4919				} else {
4920					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6
4921				}
4922			}
4923			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4924				return 0;
4925			}
4926		}
4927		if (!same_ops && (op2_info & MAY_BE_LONG)) {
4928			if (op2_info & MAY_BE_DOUBLE) {
4929				|.cold_code
4930			}
4931		    |1:
4932			if (op2_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
4933				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
4934			}
4935			if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4936				return 0;
4937			}
4938			if (op2_info & MAY_BE_DOUBLE) {
4939				|	jmp >5
4940				|.code
4941			}
4942		}
4943	} else if ((op2_info & MAY_BE_DOUBLE) &&
4944	           !(op2_info & MAY_BE_LONG) &&
4945	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4946	           (res_info & MAY_BE_DOUBLE)) {
4947		if (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
4948			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6
4949		}
4950		if (op1_info & MAY_BE_DOUBLE) {
4951			if (!same_ops && (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4952				if (!same_ops && (op1_info & MAY_BE_LONG)) {
4953					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >1
4954				} else {
4955					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6
4956				}
4957			}
4958			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4959				return 0;
4960			}
4961		}
4962		if (!same_ops && (op1_info & MAY_BE_LONG)) {
4963			if (op1_info & MAY_BE_DOUBLE) {
4964				|.cold_code
4965			}
4966			|1:
4967			if (op1_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
4968				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
4969			}
4970			if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4971				return 0;
4972			}
4973			if (op1_info & MAY_BE_DOUBLE) {
4974				|	jmp >5
4975				|.code
4976			}
4977		}
4978	}
4979
4980	|5:
4981
4982	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
4983		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
4984		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4985		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4986		    (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
4987			|.cold_code
4988		}
4989		|6:
4990		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
4991			if (Z_MODE(res_addr) == IS_REG) {
4992				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4993				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
4994			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
4995				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
4996			}
4997			if (Z_MODE(op1_addr) == IS_REG) {
4998				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
4999				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
5000					return 0;
5001				}
5002				op1_addr = real_addr;
5003			}
5004			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5005		} else {
5006			if (Z_MODE(op1_addr) == IS_REG) {
5007				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
5008				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
5009					return 0;
5010				}
5011				op1_addr = real_addr;
5012			}
5013			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5014			if (Z_MODE(res_addr) == IS_REG) {
5015				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5016				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
5017			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5018				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5019			}
5020		}
5021
5022		if (Z_MODE(op2_addr) == IS_REG) {
5023			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
5024			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
5025				return 0;
5026			}
5027			op2_addr = real_addr;
5028		}
5029		|.if X64
5030			|	LOAD_ZVAL_ADDR CARG3, op2_addr
5031		|.else
5032			|	sub r4, 12
5033			|	PUSH_ZVAL_ADDR op2_addr, r0
5034		|.endif
5035		|	SET_EX_OPLINE opline, r0
5036		if (opcode == ZEND_ADD) {
5037			|	EXT_CALL add_function, r0
5038		} else if (opcode == ZEND_SUB) {
5039			|	EXT_CALL sub_function, r0
5040		} else if (opcode == ZEND_MUL) {
5041			|	EXT_CALL mul_function, r0
5042		} else if (opcode == ZEND_DIV) {
5043			|	EXT_CALL div_function, r0
5044		} else {
5045			ZEND_UNREACHABLE();
5046		}
5047		|.if not(X64)
5048		|	add r4, 12
5049		|.endif
5050		|	FREE_OP op1_type, op1, op1_info, 0, NULL
5051		|	FREE_OP op2_type, op2, op2_info, 0, NULL
5052		if (may_throw) {
5053			if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
5054				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
5055				|	jne ->exception_handler_free_op2
5056			} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
5057				zend_jit_check_exception_undef_result(Dst, opline);
5058			} else {
5059				zend_jit_check_exception(Dst);
5060			}
5061		}
5062		if (Z_MODE(res_addr) == IS_REG) {
5063			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5064			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
5065				return 0;
5066			}
5067		}
5068		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
5069		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
5070		    (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
5071			|	jmp <5
5072			|.code
5073		}
5074	}
5075
5076	return 1;
5077}
5078
5079static 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)
5080{
5081	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
5082	ZEND_ASSERT((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
5083	    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)));
5084
5085	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)) {
5086		return 0;
5087	}
5088	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
5089		return 0;
5090	}
5091	return 1;
5092}
5093
5094static 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)
5095{
5096	if (Z_MODE(op2_addr) != IS_MEM_ZVAL || Z_REG(op2_addr) != ZREG_FCARG1) {
5097		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
5098		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5099	} else if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG2) {
5100		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5101		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
5102	} else {
5103		|	GET_ZVAL_LVAL ZREG_R0, op2_addr
5104		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
5105		|	mov FCARG2a, r0
5106	}
5107	|	EXT_CALL zend_jit_add_arrays_helper, r0
5108	|	SET_ZVAL_PTR res_addr, r0
5109	|	SET_ZVAL_TYPE_INFO res_addr, IS_ARRAY_EX
5110	|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
5111	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
5112	return 1;
5113}
5114
5115static int zend_jit_long_math_helper(dasm_State    **Dst,
5116                                     const zend_op  *opline,
5117                                     zend_uchar      opcode,
5118                                     zend_uchar      op1_type,
5119                                     znode_op        op1,
5120                                     zend_jit_addr   op1_addr,
5121                                     uint32_t        op1_info,
5122                                     zend_ssa_range *op1_range,
5123                                     zend_uchar      op2_type,
5124                                     znode_op        op2,
5125                                     zend_jit_addr   op2_addr,
5126                                     uint32_t        op2_info,
5127                                     zend_ssa_range *op2_range,
5128                                     uint32_t        res_var,
5129                                     zend_jit_addr   res_addr,
5130                                     uint32_t        res_info,
5131                                     uint32_t        res_use_info,
5132                                     int             may_throw)
5133/* Labels: 6 */
5134{
5135	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
5136	zend_reg result_reg;
5137
5138	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
5139		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
5140	}
5141	if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
5142		|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6
5143	}
5144
5145	if (opcode == ZEND_MOD) {
5146		result_reg = ZREG_RAX;
5147	} else if (Z_MODE(res_addr) == IS_REG) {
5148		if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR)
5149		 && opline->op2_type != IS_CONST) {
5150			result_reg = ZREG_R0;
5151		} else {
5152			result_reg = Z_REG(res_addr);
5153		}
5154	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
5155		result_reg = Z_REG(op1_addr);
5156	} else if (Z_REG(res_addr) != ZREG_R0) {
5157		result_reg = ZREG_R0;
5158	} else {
5159		/* ASSIGN_DIM_OP */
5160		if (ZREG_FCARG1 == ZREG_RCX
5161		 && (opcode == ZEND_SL || opcode == ZEND_SR)
5162		 && Z_MODE(op2_addr) != IS_CONST_ZVAL) {
5163			result_reg = ZREG_R2;
5164		} else {
5165			result_reg = ZREG_FCARG1;
5166		}
5167	}
5168
5169	if (opcode == ZEND_SL) {
5170		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5171			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
5172
5173			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
5174				if (EXPECTED(op2_lval > 0)) {
5175					|	xor Ra(result_reg), Ra(result_reg)
5176				} else {
5177					zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5178					zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5179					|	SET_EX_OPLINE opline, r0
5180					|	jmp ->negative_shift
5181				}
5182			} else if (Z_MODE(op1_addr) == IS_REG && op2_lval == 1) {
5183				|	lea Ra(result_reg), [Ra(Z_REG(op1_addr))+Ra(Z_REG(op1_addr))]
5184			} else {
5185				|	GET_ZVAL_LVAL result_reg, op1_addr
5186				|	shl Ra(result_reg), op2_lval
5187			}
5188		} else {
5189			if (Z_MODE(op2_addr) != IS_REG || Z_REG(op2_addr) != ZREG_RCX) {
5190				|	GET_ZVAL_LVAL ZREG_RCX, op2_addr
5191			}
5192			if (!op2_range ||
5193			     op2_range->min < 0 ||
5194			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
5195				|	cmp r1, (SIZEOF_ZEND_LONG*8)
5196				|	jae >1
5197				|.cold_code
5198				|1:
5199				|	cmp r1, 0
5200				|	mov Ra(result_reg), 0
5201				|	jg >1
5202				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5203				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5204				|	SET_EX_OPLINE opline, r0
5205				|	jmp ->negative_shift
5206				|.code
5207			}
5208			|	GET_ZVAL_LVAL result_reg, op1_addr
5209			|	shl Ra(result_reg), cl
5210			|1:
5211		}
5212	} else if (opcode == ZEND_SR) {
5213		|	GET_ZVAL_LVAL result_reg, op1_addr
5214		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5215			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
5216
5217			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
5218				if (EXPECTED(op2_lval > 0)) {
5219					|	sar Ra(result_reg), (SIZEOF_ZEND_LONG * 8) - 1
5220				} else {
5221					zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5222					zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5223					|	SET_EX_OPLINE opline, r0
5224					|	jmp ->negative_shift
5225				}
5226			} else {
5227				|	sar Ra(result_reg), op2_lval
5228			}
5229		} else {
5230			if (Z_MODE(op2_addr) != IS_REG || Z_REG(op2_addr) != ZREG_RCX) {
5231				|	GET_ZVAL_LVAL ZREG_RCX, op2_addr
5232			}
5233			if (!op2_range ||
5234			     op2_range->min < 0 ||
5235			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
5236				|	cmp r1, (SIZEOF_ZEND_LONG*8)
5237				|	jae >1
5238				|.cold_code
5239				|1:
5240				|	cmp r1, 0
5241				|	mov r1, (SIZEOF_ZEND_LONG * 8) - 1
5242				|	jg >1
5243				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5244				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5245				|	SET_EX_OPLINE opline, r0
5246				|	jmp ->negative_shift
5247				|.code
5248			}
5249			|1:
5250			|	sar Ra(result_reg), cl
5251		}
5252	} else if (opcode == ZEND_MOD) {
5253		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5254			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
5255
5256			if (op2_lval == 0) {
5257				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5258				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5259				|	SET_EX_OPLINE opline, r0
5260				|	jmp ->mod_by_zero
5261			} else if (zend_long_is_power_of_two(op2_lval) && op1_range && op1_range->min >= 0) {
5262				zval tmp;
5263				zend_jit_addr tmp_addr;
5264				zend_reg tmp_reg;
5265
5266				/* Optimisation for mod of power of 2 */
5267				ZVAL_LONG(&tmp, op2_lval - 1);
5268				tmp_addr = ZEND_ADDR_CONST_ZVAL(&tmp);
5269				if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
5270					tmp_reg = ZREG_R1;
5271				} else if (result_reg != ZREG_R0) {
5272					tmp_reg = ZREG_R0;
5273				} else {
5274					tmp_reg = ZREG_R1;
5275				}
5276				|	GET_ZVAL_LVAL result_reg, op1_addr
5277				|	LONG_MATH ZEND_BW_AND, result_reg, tmp_addr, tmp_reg
5278				(void)tmp_reg;
5279			} else {
5280				if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5281					|	mov aword T1, r0 // save
5282				} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RCX) {
5283					|	mov aword T1, Ra(ZREG_RCX) // save
5284				}
5285				result_reg = ZREG_RDX;
5286				if (op2_lval == -1) {
5287					|	xor Ra(result_reg), Ra(result_reg)
5288				} else {
5289					|	GET_ZVAL_LVAL ZREG_RAX, op1_addr
5290					|	GET_ZVAL_LVAL ZREG_RCX, op2_addr
5291					|.if X64
5292					|	cqo
5293					|.else
5294					|	cdq
5295					|.endif
5296					|	idiv Ra(ZREG_RCX)
5297				}
5298				if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5299					|	mov r0, aword T1 // restore
5300				} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RCX) {
5301					|	mov Ra(ZREG_RCX), aword T1 // restore
5302				}
5303			}
5304		} else {
5305			if ((op2_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) || !op2_range || (op2_range->min <= 0 && op2_range->max >= 0)) {
5306				if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
5307					|	cmp aword [Ra(Z_REG(op2_addr))+Z_OFFSET(op2_addr)], 0
5308				} else if (Z_MODE(op2_addr) == IS_REG) {
5309					|	test Ra(Z_REG(op2_addr)), Ra(Z_REG(op2_addr))
5310				}
5311				|	jz >1
5312				|.cold_code
5313				|1:
5314				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
5315				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
5316				|	SET_EX_OPLINE opline, r0
5317				|	jmp ->mod_by_zero
5318				|.code
5319			}
5320
5321			/* Prevent overflow error/crash if op1 == LONG_MIN and op2 == -1 */
5322			if (!op2_range || (op2_range->min <= -1 && op2_range->max >= -1)) {
5323				if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
5324					|	cmp aword [Ra(Z_REG(op2_addr))+Z_OFFSET(op2_addr)], -1
5325				} else if (Z_MODE(op2_addr) == IS_REG) {
5326					|	cmp Ra(Z_REG(op2_addr)), -1
5327				}
5328				|	jz >1
5329				|.cold_code
5330				|1:
5331				|	SET_ZVAL_LVAL res_addr, 0
5332				if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
5333					if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
5334						if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
5335							|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
5336						}
5337					}
5338				}
5339				|	jmp >5
5340				|.code
5341			}
5342
5343			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5344				|	mov aword T1, r0 // save
5345			}
5346			result_reg = ZREG_RDX;
5347			|	GET_ZVAL_LVAL ZREG_RAX, op1_addr
5348			|.if X64
5349			|	cqo
5350			|.else
5351			|	cdq
5352			|.endif
5353			if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
5354				|	idiv aword [Ra(Z_REG(op2_addr))+Z_OFFSET(op2_addr)]
5355			} else if (Z_MODE(op2_addr) == IS_REG) {
5356				|	idiv Ra(Z_REG(op2_addr))
5357			}
5358			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) {
5359				|	mov r0, aword T1 // restore
5360			}
5361		}
5362	} else if (same_ops) {
5363		|	GET_ZVAL_LVAL result_reg, op1_addr
5364		|	LONG_MATH_REG opcode, Ra(result_reg), Ra(result_reg)
5365	} else {
5366		zend_reg tmp_reg;
5367
5368		if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) {
5369			tmp_reg = ZREG_R1;
5370		} else if (result_reg != ZREG_R0) {
5371			tmp_reg = ZREG_R0;
5372		} else {
5373			tmp_reg = ZREG_R1;
5374		}
5375		|	GET_ZVAL_LVAL result_reg, op1_addr
5376		|	LONG_MATH opcode, result_reg, op2_addr, tmp_reg
5377		(void)tmp_reg;
5378	}
5379
5380	if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != result_reg) {
5381		|	SET_ZVAL_LVAL res_addr, Ra(result_reg)
5382	}
5383	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
5384		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
5385			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
5386				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
5387			}
5388		}
5389	}
5390
5391	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) ||
5392		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
5393		if ((op1_info & MAY_BE_LONG) &&
5394		    (op2_info & MAY_BE_LONG)) {
5395			|.cold_code
5396		}
5397		|6:
5398		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
5399			if (Z_MODE(res_addr) == IS_REG) {
5400				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5401				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
5402			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5403				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5404			}
5405			if (Z_MODE(op1_addr) == IS_REG) {
5406				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
5407				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
5408					return 0;
5409				}
5410				op1_addr = real_addr;
5411			}
5412			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5413		} else {
5414			if (Z_MODE(op1_addr) == IS_REG) {
5415				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
5416				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
5417					return 0;
5418				}
5419				op1_addr = real_addr;
5420			}
5421			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5422			if (Z_MODE(res_addr) == IS_REG) {
5423				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5424				|	LOAD_ZVAL_ADDR FCARG1a, real_addr
5425			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5426				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5427			}
5428		}
5429		if (Z_MODE(op2_addr) == IS_REG) {
5430			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
5431			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
5432				return 0;
5433			}
5434			op2_addr = real_addr;
5435		}
5436		|.if X64
5437			|	LOAD_ZVAL_ADDR CARG3, op2_addr
5438		|.else
5439			|	sub r4, 12
5440			|	PUSH_ZVAL_ADDR op2_addr, r0
5441		|.endif
5442		|	SET_EX_OPLINE opline, r0
5443		if (opcode == ZEND_BW_OR) {
5444			|	EXT_CALL bitwise_or_function, r0
5445		} else if (opcode == ZEND_BW_AND) {
5446			|	EXT_CALL bitwise_and_function, r0
5447		} else if (opcode == ZEND_BW_XOR) {
5448			|	EXT_CALL bitwise_xor_function, r0
5449		} else if (opcode == ZEND_SL) {
5450			|	EXT_CALL shift_left_function, r0
5451		} else if (opcode == ZEND_SR) {
5452			|	EXT_CALL shift_right_function, r0
5453		} else if (opcode == ZEND_MOD) {
5454			|	EXT_CALL mod_function, r0
5455		} else {
5456			ZEND_UNREACHABLE();
5457		}
5458		|.if not(X64)
5459		|	add r4, 12
5460		|.endif
5461		if (op1_addr == res_addr && (op2_info & MAY_BE_RCN)) {
5462			/* compound assignment may decrement "op2" refcount */
5463			op2_info |= MAY_BE_RC1;
5464		}
5465		|	FREE_OP op1_type, op1, op1_info, 0, NULL
5466		|	FREE_OP op2_type, op2, op2_info, 0, NULL
5467		if (may_throw) {
5468			if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
5469				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
5470				|	jne ->exception_handler_free_op2
5471			} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
5472				zend_jit_check_exception_undef_result(Dst, opline);
5473			} else {
5474				zend_jit_check_exception(Dst);
5475			}
5476		}
5477		if (Z_MODE(res_addr) == IS_REG) {
5478			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
5479			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
5480				return 0;
5481			}
5482		}
5483		if ((op1_info & MAY_BE_LONG) &&
5484		    (op2_info & MAY_BE_LONG)) {
5485			|	jmp >5
5486			|.code
5487		}
5488	}
5489	|5:
5490
5491	return 1;
5492}
5493
5494static 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)
5495{
5496	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
5497	ZEND_ASSERT((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG));
5498
5499	if (!zend_jit_long_math_helper(Dst, opline, opline->opcode,
5500			opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
5501			opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
5502			opline->result.var, res_addr, res_info, res_use_info, may_throw)) {
5503		return 0;
5504	}
5505	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
5506		return 0;
5507	}
5508	return 1;
5509}
5510
5511static int zend_jit_concat_helper(dasm_State    **Dst,
5512                                  const zend_op  *opline,
5513                                  zend_uchar      op1_type,
5514                                  znode_op        op1,
5515                                  zend_jit_addr   op1_addr,
5516                                  uint32_t        op1_info,
5517                                  zend_uchar      op2_type,
5518                                  znode_op        op2,
5519                                  zend_jit_addr   op2_addr,
5520                                  uint32_t        op2_info,
5521                                  zend_jit_addr   res_addr,
5522                                  int             may_throw)
5523{
5524#if 1
5525	if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5526		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
5527			|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6
5528		}
5529		if (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
5530			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >6
5531		}
5532		if (Z_MODE(op1_addr) == IS_MEM_ZVAL && Z_REG(op1_addr) == Z_REG(res_addr) && Z_OFFSET(op1_addr) == Z_OFFSET(res_addr)) {
5533			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5534				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5535			}
5536			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
5537			|	EXT_CALL zend_jit_fast_assign_concat_helper, r0
5538			/* concatination with itself may reduce refcount */
5539			op2_info |= MAY_BE_RC1;
5540		} else {
5541			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5542				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5543			}
5544			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5545			|.if X64
5546				|	LOAD_ZVAL_ADDR CARG3, op2_addr
5547			|.else
5548				|	sub r4, 12
5549				|	PUSH_ZVAL_ADDR op2_addr, r0
5550			|.endif
5551			if (op1_type == IS_CV || op1_type == IS_CONST) {
5552				|	EXT_CALL zend_jit_fast_concat_helper, r0
5553			} else {
5554				|	EXT_CALL zend_jit_fast_concat_tmp_helper, r0
5555			}
5556			|.if not(X64)
5557			|	add r4, 12
5558			|.endif
5559		}
5560		/* concatination with empty string may increase refcount */
5561		op2_info |= MAY_BE_RCN;
5562		|	FREE_OP op2_type, op2, op2_info, 0, opline
5563		|5:
5564	}
5565	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) ||
5566	    (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING))) {
5567		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5568			|.cold_code
5569			|6:
5570		}
5571#endif
5572		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
5573			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5574				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5575			}
5576			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5577		} else {
5578			|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
5579			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5580				|	LOAD_ZVAL_ADDR FCARG1a, res_addr
5581			}
5582		}
5583		|.if X64
5584			|	LOAD_ZVAL_ADDR CARG3, op2_addr
5585		|.else
5586			|	sub r4, 12
5587			|	PUSH_ZVAL_ADDR op2_addr, r0
5588		|.endif
5589		|	SET_EX_OPLINE opline, r0
5590		|	EXT_CALL concat_function, r0
5591		|.if not(X64)
5592		|	add r4, 12
5593		|.endif
5594		/* concatination with empty string may increase refcount */
5595		op1_info |= MAY_BE_RCN;
5596		op2_info |= MAY_BE_RCN;
5597		|	FREE_OP op1_type, op1, op1_info, 0, NULL
5598		|	FREE_OP op2_type, op2, op2_info, 0, NULL
5599		if (may_throw) {
5600			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
5601				zend_jit_check_exception_undef_result(Dst, opline);
5602			} else {
5603				zend_jit_check_exception(Dst);
5604			}
5605		}
5606#if 1
5607		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5608			|	jmp <5
5609			|.code
5610		}
5611	}
5612#endif
5613
5614	return 1;
5615}
5616
5617static 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)
5618{
5619	zend_jit_addr op1_addr, op2_addr;
5620
5621	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
5622	ZEND_ASSERT((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING));
5623
5624	op1_addr = OP1_ADDR();
5625	op2_addr = OP2_ADDR();
5626
5627	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);
5628}
5629
5630static 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)
5631/* Labels: 1,2,3,4,5 */
5632{
5633	zend_jit_addr op2_addr = OP2_ADDR();
5634	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
5635
5636	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
5637	 && type == BP_VAR_R
5638	 && !exit_addr) {
5639		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
5640		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
5641		if (!exit_addr) {
5642			return 0;
5643		}
5644	}
5645
5646	if (op2_info & MAY_BE_LONG) {
5647		bool op2_loaded = 0;
5648		bool packed_loaded = 0;
5649		bool bad_packed_key = 0;
5650
5651		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) {
5652			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_LONG))
5653			|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3
5654		}
5655		if (op1_info & MAY_BE_PACKED_GUARD) {
5656			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
5657			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
5658
5659			if (!exit_addr) {
5660				return 0;
5661			}
5662			if (op1_info & MAY_BE_ARRAY_PACKED) {
5663				|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5664				|	jz &exit_addr
5665			} else {
5666				|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5667				|	jnz &exit_addr
5668			}
5669		}
5670		if (type == BP_VAR_W) {
5671			|	// hval = Z_LVAL_P(dim);
5672			|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5673			op2_loaded = 1;
5674		}
5675		if (op1_info & MAY_BE_ARRAY_PACKED) {
5676			zend_long val = -1;
5677
5678			if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5679				val = Z_LVAL_P(Z_ZV(op2_addr));
5680				if (val >= 0 && val < HT_MAX_SIZE) {
5681					packed_loaded = 1;
5682				} else {
5683					bad_packed_key = 1;
5684				}
5685			} else {
5686				if (!op2_loaded) {
5687					|	// hval = Z_LVAL_P(dim);
5688					|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5689					op2_loaded = 1;
5690				}
5691				packed_loaded = 1;
5692			}
5693
5694			if (dim_type == IS_UNDEF && type == BP_VAR_W && packed_loaded) {
5695				/* don't generate "fast" code for packed array */
5696				packed_loaded = 0;
5697			}
5698
5699			if (packed_loaded) {
5700				|	// ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
5701				if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5702					|	test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5703					|	jz >4 // HASH_FIND
5704				}
5705				|	// if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed))
5706				|.if X64
5707					|	mov eax, dword [FCARG1a + offsetof(zend_array, nNumUsed)]
5708					if (val == 0) {
5709						|	test r0, r0
5710					} else if (val > 0 && !op2_loaded) {
5711						|	cmp r0, val
5712					} else {
5713						|	cmp r0, FCARG2a
5714					}
5715				|.else
5716					if (val >= 0 && !op2_loaded) {
5717						|	cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], val
5718					} else {
5719						|	cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], FCARG2a
5720					}
5721				|.endif
5722				if (type == BP_JIT_IS) {
5723					if (not_found_exit_addr) {
5724						|	jbe &not_found_exit_addr
5725					} else {
5726						|	jbe >9 // NOT_FOUND
5727					}
5728				} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5729					|	jbe &exit_addr
5730				} else if (type == BP_VAR_IS && not_found_exit_addr) {
5731					|	jbe &not_found_exit_addr
5732				} else if (type == BP_VAR_RW && not_found_exit_addr) {
5733					|	jbe &not_found_exit_addr
5734				} else if (type == BP_VAR_IS && found_exit_addr) {
5735					|	jbe >7 // NOT_FOUND
5736				} else {
5737					|	jbe >2 // NOT_FOUND
5738				}
5739				|	// _ret = &_ht->arData[_h].val;
5740				if (val >= 0) {
5741					|	mov r0, aword [FCARG1a + offsetof(zend_array, arData)]
5742					if (val != 0) {
5743						|	add r0, val * sizeof(Bucket)
5744					}
5745				} else {
5746					|.if X64
5747						|	mov r0, FCARG2a
5748						|	shl r0, 5
5749					|.else
5750						|	imul r0, FCARG2a, sizeof(Bucket)
5751					|.endif
5752					|	add r0, aword [FCARG1a + offsetof(zend_array, arData)]
5753				}
5754			}
5755		}
5756		switch (type) {
5757			case BP_JIT_IS:
5758				if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5759					if (packed_loaded) {
5760						|	jmp >5
5761					}
5762					|4:
5763					if (!op2_loaded) {
5764						|	// hval = Z_LVAL_P(dim);
5765						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5766					}
5767					if (packed_loaded) {
5768						|	EXT_CALL _zend_hash_index_find, r0
5769					} else {
5770						|	EXT_CALL zend_hash_index_find, r0
5771					}
5772					|	test r0, r0
5773					if (not_found_exit_addr) {
5774						|	jz &not_found_exit_addr
5775					} else {
5776						|	jz >9 // NOT_FOUND
5777					}
5778					if (op2_info & MAY_BE_STRING) {
5779						|	jmp >5
5780					}
5781				} else if (packed_loaded) {
5782					if (op2_info & MAY_BE_STRING) {
5783						|	jmp >5
5784					}
5785				} else if (not_found_exit_addr) {
5786					|	jmp &not_found_exit_addr
5787				} else {
5788					|	jmp >9 // NOT_FOUND
5789				}
5790				break;
5791			case BP_VAR_R:
5792			case BP_VAR_IS:
5793			case BP_VAR_UNSET:
5794				if (packed_loaded) {
5795					if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5796						|	IF_NOT_Z_TYPE r0, IS_UNDEF, >8
5797					} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5798						/* perform IS_UNDEF check only after result type guard (during deoptimization) */
5799						if (!found_exit_addr || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5800							|	IF_Z_TYPE r0, IS_UNDEF, &exit_addr
5801						}
5802					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5803						|	IF_Z_TYPE r0, IS_UNDEF, &not_found_exit_addr
5804					} else if (type == BP_VAR_IS && found_exit_addr) {
5805						|	IF_Z_TYPE r0, IS_UNDEF, >7 // NOT_FOUND
5806					} else {
5807						|	IF_Z_TYPE r0, IS_UNDEF, >2 // NOT_FOUND
5808					}
5809				}
5810				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (packed_loaded && (op1_info & MAY_BE_ARRAY_NUMERIC_HASH))) {
5811					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5812						|	jmp &exit_addr
5813					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5814						|	jmp &not_found_exit_addr
5815					} else if (type == BP_VAR_IS && found_exit_addr) {
5816						|	jmp >7 // NOT_FOUND
5817					} else {
5818						|	jmp >2 // NOT_FOUND
5819					}
5820				}
5821				if (!packed_loaded || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5822					|4:
5823					if (!op2_loaded) {
5824						|	// hval = Z_LVAL_P(dim);
5825						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5826					}
5827					if (packed_loaded) {
5828						|	EXT_CALL _zend_hash_index_find, r0
5829					} else {
5830						|	EXT_CALL zend_hash_index_find, r0
5831					}
5832					|	test r0, r0
5833					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5834						|	jz &exit_addr
5835					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5836						|	jz &not_found_exit_addr
5837					} else if (type == BP_VAR_IS && found_exit_addr) {
5838						|	jz >7 // NOT_FOUND
5839					} else {
5840						|	jz >2 // NOT_FOUND
5841					}
5842				}
5843				|.cold_code
5844				|2:
5845				switch (type) {
5846					case BP_VAR_R:
5847						if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
5848							|	// zend_error(E_WARNING,"Undefined array key " ZEND_LONG_FMT, hval);
5849							|	// retval = &EG(uninitialized_zval);
5850							|	UNDEFINED_OFFSET opline
5851							|	jmp >9
5852						}
5853						break;
5854					case BP_VAR_IS:
5855					case BP_VAR_UNSET:
5856						if (!not_found_exit_addr && !found_exit_addr) {
5857							|	// retval = &EG(uninitialized_zval);
5858							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
5859							|	jmp >9
5860						}
5861						break;
5862					default:
5863						ZEND_UNREACHABLE();
5864				}
5865				|.code
5866				break;
5867			case BP_VAR_RW:
5868				if (packed_loaded && !not_found_exit_addr) {
5869					|	IF_NOT_Z_TYPE r0, IS_UNDEF, >8
5870				}
5871				if (!packed_loaded ||
5872						!not_found_exit_addr ||
5873						(op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5874					if (packed_loaded && not_found_exit_addr) {
5875						|.cold_code
5876					}
5877					|2:
5878					|4:
5879					if (!op2_loaded) {
5880						|	// hval = Z_LVAL_P(dim);
5881						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5882					}
5883					if (packed_loaded) {
5884						|	EXT_CALL zend_jit_hash_index_lookup_rw_no_packed, r0
5885					} else {
5886						|	EXT_CALL zend_jit_hash_index_lookup_rw, r0
5887					}
5888					|	test r0, r0
5889					if (not_found_exit_addr) {
5890						if (packed_loaded) {
5891							|	jnz >8
5892							|	jmp &not_found_exit_addr
5893							|.code
5894						} else {
5895							|	jz &not_found_exit_addr
5896						}
5897					} else {
5898						|	jz >9
5899					}
5900				}
5901				break;
5902			case BP_VAR_W:
5903				if (packed_loaded) {
5904					|	IF_NOT_Z_TYPE r0, IS_UNDEF, >8
5905				}
5906				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) || packed_loaded || bad_packed_key || dim_type == IS_UNDEF) {
5907					|2:
5908					|4:
5909					if (!op2_loaded) {
5910						|	// hval = Z_LVAL_P(dim);
5911						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5912					}
5913					|	EXT_CALL zend_hash_index_lookup, r0
5914				}
5915				break;
5916			default:
5917				ZEND_UNREACHABLE();
5918		}
5919
5920		if (type != BP_JIT_IS && (op2_info & MAY_BE_STRING)) {
5921			|	jmp >8
5922		}
5923	}
5924
5925	if (op2_info & MAY_BE_STRING) {
5926		|3:
5927		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
5928			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_STRING))
5929			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >3
5930		}
5931		|	// offset_key = Z_STR_P(dim);
5932		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
5933		|	// retval = zend_hash_find(ht, offset_key);
5934		switch (type) {
5935			case BP_JIT_IS:
5936				if (opline->op2_type != IS_CONST) {
5937					|	cmp byte [FCARG2a + offsetof(zend_string, val)], '9'
5938					|	jle >1
5939					|.cold_code
5940					|1:
5941					|	EXT_CALL zend_jit_symtable_find, r0
5942					|	jmp >1
5943					|.code
5944					|	EXT_CALL zend_hash_find, r0
5945					|1:
5946				} else {
5947					|	EXT_CALL zend_hash_find_known_hash, r0
5948				}
5949				|	test r0, r0
5950				if (not_found_exit_addr) {
5951					|	jz &not_found_exit_addr
5952				} else {
5953					|	jz >9 // NOT_FOUND
5954				}
5955				break;
5956			case BP_VAR_R:
5957			case BP_VAR_IS:
5958			case BP_VAR_UNSET:
5959				if (opline->op2_type != IS_CONST) {
5960					|	cmp byte [FCARG2a + offsetof(zend_string, val)], '9'
5961					|	jle >1
5962					|.cold_code
5963					|1:
5964					|	EXT_CALL zend_jit_symtable_find, r0
5965					|	jmp >1
5966					|.code
5967					|	EXT_CALL zend_hash_find, r0
5968					|1:
5969				} else {
5970					|	EXT_CALL zend_hash_find_known_hash, r0
5971				}
5972				|	test r0, r0
5973				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5974					|	jz &exit_addr
5975				} else if (type == BP_VAR_IS && not_found_exit_addr) {
5976					|	jz &not_found_exit_addr
5977				} else if (type == BP_VAR_IS && found_exit_addr) {
5978					|	jz >7 // NOT_FOUND
5979				} else {
5980					|	jz >2 // NOT_FOUND
5981					|.cold_code
5982					|2:
5983					switch (type) {
5984						case BP_VAR_R:
5985							// zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
5986							|	UNDEFINED_INDEX opline
5987							|	jmp >9
5988							break;
5989						case BP_VAR_IS:
5990						case BP_VAR_UNSET:
5991							|	// retval = &EG(uninitialized_zval);
5992							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
5993							|	jmp >9
5994							break;
5995						default:
5996							ZEND_UNREACHABLE();
5997					}
5998					|.code
5999				}
6000				break;
6001			case BP_VAR_RW:
6002				if (opline->op2_type != IS_CONST) {
6003					|	EXT_CALL zend_jit_symtable_lookup_rw, r0
6004				} else {
6005					|	EXT_CALL zend_jit_hash_lookup_rw, r0
6006				}
6007				|	test r0, r0
6008				if (not_found_exit_addr) {
6009					|	jz &not_found_exit_addr
6010				} else {
6011					|	jz >9
6012				}
6013				break;
6014			case BP_VAR_W:
6015				if (opline->op2_type != IS_CONST) {
6016					|	EXT_CALL zend_jit_symtable_lookup_w, r0
6017				} else {
6018					|	EXT_CALL zend_hash_lookup, r0
6019				}
6020				break;
6021			default:
6022				ZEND_UNREACHABLE();
6023		}
6024	}
6025
6026	if (type == BP_JIT_IS && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))) {
6027	    |5:
6028		if (op1_info & MAY_BE_ARRAY_OF_REF) {
6029			|	ZVAL_DEREF r0, MAY_BE_REF
6030		}
6031		|	cmp byte [r0 + 8], IS_NULL
6032		if (not_found_exit_addr) {
6033			|	jle &not_found_exit_addr
6034		} else if (found_exit_addr) {
6035			|	jg &found_exit_addr
6036		} else {
6037			|	jle >9 // NOT FOUND
6038		}
6039	}
6040
6041	if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
6042		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6043			|.cold_code
6044			|3:
6045		}
6046		if (type != BP_VAR_RW) {
6047			|	SET_EX_OPLINE opline, r0
6048		}
6049		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
6050		switch (type) {
6051			case BP_VAR_R:
6052				|.if X64
6053					|   LOAD_ZVAL_ADDR CARG3, res_addr
6054				|.else
6055					|	sub r4, 12
6056					|   PUSH_ZVAL_ADDR res_addr, r0
6057				|.endif
6058				|	EXT_CALL zend_jit_fetch_dim_r_helper, r0
6059				|.if not(X64)
6060				|	add r4, 12
6061				|.endif
6062				|	jmp >9
6063				break;
6064			case BP_JIT_IS:
6065				|	EXT_CALL zend_jit_fetch_dim_isset_helper, r0
6066				|	test r0, r0
6067				if (not_found_exit_addr) {
6068					|	je &not_found_exit_addr
6069					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6070						|	jmp >8
6071					}
6072				} else if (found_exit_addr) {
6073					|	jne &found_exit_addr
6074					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6075						|	jmp >9
6076					}
6077				} else {
6078					|	jne >8
6079					|	jmp >9
6080				}
6081				break;
6082			case BP_VAR_IS:
6083			case BP_VAR_UNSET:
6084				|.if X64
6085					|   LOAD_ZVAL_ADDR CARG3, res_addr
6086				|.else
6087					|	sub r4, 12
6088					|   PUSH_ZVAL_ADDR res_addr, r0
6089				|.endif
6090				|	EXT_CALL zend_jit_fetch_dim_is_helper, r0
6091				|.if not(X64)
6092				|	add r4, 12
6093				|.endif
6094				|	jmp >9
6095				break;
6096			case BP_VAR_RW:
6097				|	EXT_CALL zend_jit_fetch_dim_rw_helper, r0
6098				|	test r0, r0
6099				|	jne >8
6100				|	jmp >9
6101				break;
6102			case BP_VAR_W:
6103				|	EXT_CALL zend_jit_fetch_dim_w_helper, r0
6104				|	test r0, r0
6105				|	jne >8
6106				|	jmp >9
6107				break;
6108			default:
6109				ZEND_UNREACHABLE();
6110		}
6111		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
6112			|.code
6113		}
6114	}
6115
6116	return 1;
6117}
6118
6119static int zend_jit_simple_assign(dasm_State    **Dst,
6120                                  const zend_op  *opline,
6121                                  zend_jit_addr   var_addr,
6122                                  uint32_t        var_info,
6123                                  uint32_t        var_def_info,
6124                                  zend_uchar      val_type,
6125                                  zend_jit_addr   val_addr,
6126                                  uint32_t        val_info,
6127                                  zend_jit_addr   res_addr,
6128                                  int             in_cold,
6129                                  int             save_r1,
6130                                  bool            check_exception)
6131/* Labels: 1,2,3 */
6132{
6133	zend_reg tmp_reg;
6134
6135	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_addr) != ZREG_R0) {
6136		tmp_reg = ZREG_R0;
6137	} else {
6138		/* ASSIGN_DIM */
6139		tmp_reg = ZREG_FCARG1;
6140	}
6141
6142	if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
6143		zval *zv = Z_ZV(val_addr);
6144
6145		if (!res_addr) {
6146			|	ZVAL_COPY_CONST var_addr, var_info, var_def_info, zv, tmp_reg
6147		} else {
6148			|	ZVAL_COPY_CONST_2 var_addr, res_addr, var_info, var_def_info, zv, tmp_reg
6149		}
6150		if (Z_REFCOUNTED_P(zv)) {
6151			if (!res_addr) {
6152				|	ADDREF_CONST zv, Ra(tmp_reg)
6153			} else {
6154				|	ADDREF_CONST_2 zv, Ra(tmp_reg)
6155			}
6156		}
6157	} else {
6158		if (val_info & MAY_BE_UNDEF) {
6159			if (in_cold) {
6160				|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >2
6161			} else {
6162				|	IF_ZVAL_TYPE val_addr, IS_UNDEF, >1
6163				|.cold_code
6164				|1:
6165			}
6166			|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
6167			if (save_r1) {
6168				|	mov aword T1, FCARG1a // save
6169			}
6170			|	SET_ZVAL_TYPE_INFO var_addr, IS_NULL
6171			if (res_addr) {
6172				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
6173			}
6174			if (opline) {
6175				|	SET_EX_OPLINE opline, Ra(tmp_reg)
6176			}
6177			ZEND_ASSERT(Z_MODE(val_addr) == IS_MEM_ZVAL && Z_REG(val_addr) == ZREG_FP);
6178			|	mov FCARG1d, Z_OFFSET(val_addr)
6179			|	EXT_CALL zend_jit_undefined_op_helper, r0
6180			if (check_exception) {
6181				|	test r0, r0
6182				|	jz ->exception_handler_undef
6183			}
6184			if (save_r1) {
6185				|	mov FCARG1a, aword T1 // restore
6186			}
6187			|	jmp >3
6188			if (in_cold) {
6189				|2:
6190			} else {
6191				|.code
6192			}
6193		}
6194		if (val_info & MAY_BE_REF) {
6195			if (val_type == IS_CV) {
6196				ZEND_ASSERT(Z_REG(var_addr) != ZREG_R2);
6197				if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_R2 || Z_OFFSET(val_addr) != 0) {
6198					|	LOAD_ZVAL_ADDR r2, val_addr
6199				}
6200				|	ZVAL_DEREF r2, val_info
6201				val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R2, 0);
6202			} else {
6203				zend_jit_addr ref_addr;
6204				zend_reg type_reg = tmp_reg;
6205
6206				if (in_cold) {
6207					|	IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, >1
6208				} else {
6209					|	IF_ZVAL_TYPE val_addr, IS_REFERENCE, >1
6210					|.cold_code
6211					|1:
6212				}
6213				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
6214				|	GET_ZVAL_PTR r2, val_addr
6215				|	GC_DELREF r2
6216				|	// ZVAL_COPY_VALUE(return_value, &ref->value);
6217				ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R2, 8);
6218				if (!res_addr) {
6219					|	ZVAL_COPY_VALUE var_addr, var_info, ref_addr, val_info, type_reg, tmp_reg
6220				} else {
6221					|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, ref_addr, val_info, type_reg, tmp_reg
6222				}
6223				|	je >2
6224				if (tmp_reg == ZREG_R0) {
6225					|	IF_NOT_REFCOUNTED ah, >3
6226				} else {
6227					|	IF_NOT_FLAGS Rd(tmp_reg), (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), >3
6228				}
6229				|	GET_ZVAL_PTR Ra(tmp_reg), var_addr
6230
6231				if (!res_addr) {
6232					|	GC_ADDREF Ra(tmp_reg)
6233				} else {
6234					|	add dword [Ra(tmp_reg)], 2
6235				}
6236				|	jmp >3
6237				|2:
6238				if (res_addr) {
6239					if (tmp_reg == ZREG_R0) {
6240						|	IF_NOT_REFCOUNTED ah, >2
6241					} else {
6242						|	IF_NOT_FLAGS Rd(tmp_reg), (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), >2
6243					}
6244					|	GET_ZVAL_PTR Ra(tmp_reg), var_addr
6245					|	GC_ADDREF Ra(tmp_reg)
6246					|2:
6247				}
6248				if (save_r1) {
6249					|	mov aword T1, FCARG1a // save
6250				}
6251				|	EFREE_REFERENCE r2
6252				if (save_r1) {
6253					|	mov FCARG1a, aword T1 // restore
6254				}
6255				|	jmp >3
6256				if (in_cold) {
6257					|1:
6258				} else {
6259					|.code
6260				}
6261			}
6262		}
6263
6264		if (!res_addr) {
6265			|	ZVAL_COPY_VALUE var_addr, var_info, val_addr, val_info, ZREG_R2, tmp_reg
6266		} else {
6267			|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, val_addr, val_info, ZREG_R2, tmp_reg
6268		}
6269
6270		if (val_type == IS_CV) {
6271			if (!res_addr) {
6272				|	TRY_ADDREF val_info, dh, Ra(tmp_reg)
6273			} else {
6274				|	TRY_ADDREF_2 val_info, dh, Ra(tmp_reg)
6275			}
6276		} else {
6277			if (res_addr) {
6278				|	TRY_ADDREF val_info, dh, Ra(tmp_reg)
6279			}
6280		}
6281		|3:
6282	}
6283	return 1;
6284}
6285
6286static int zend_jit_assign_to_typed_ref(dasm_State         **Dst,
6287                                       const zend_op        *opline,
6288                                       zend_uchar            val_type,
6289                                       zend_jit_addr         val_addr,
6290                                       zend_jit_addr         res_addr,
6291                                       bool                  check_exception)
6292{
6293	|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
6294	|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
6295	|	jnz >2
6296	|.cold_code
6297	|2:
6298	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
6299		|	LOAD_ZVAL_ADDR FCARG2a, val_addr
6300	}
6301	if (opline) {
6302		|	SET_EX_OPLINE opline, r0
6303	}
6304	if (val_type == IS_CONST) {
6305		|	EXT_CALL zend_jit_assign_const_to_typed_ref, r0
6306	} else if (val_type == IS_TMP_VAR) {
6307		|	EXT_CALL zend_jit_assign_tmp_to_typed_ref, r0
6308	} else if (val_type == IS_VAR) {
6309		|	EXT_CALL zend_jit_assign_var_to_typed_ref, r0
6310	} else if (val_type == IS_CV) {
6311		|	EXT_CALL zend_jit_assign_cv_to_typed_ref, r0
6312	} else {
6313		ZEND_UNREACHABLE();
6314	}
6315	if (res_addr) {
6316		zend_jit_addr ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
6317
6318		|	ZVAL_COPY_VALUE res_addr, -1, ret_addr, -1, ZREG_R1, ZREG_R2
6319		|	TRY_ADDREF -1, ch, r2
6320	}
6321	if (check_exception) {
6322		|	// if (UNEXPECTED(EG(exception) != NULL)) {
6323		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
6324		|	je >8  // END OF zend_jit_assign_to_variable()
6325		|	jmp ->exception_handler
6326	} else {
6327		|	jmp >8
6328	}
6329	|.code
6330
6331	return 1;
6332}
6333
6334static int zend_jit_assign_to_variable_call(dasm_State    **Dst,
6335                                            const zend_op  *opline,
6336                                            zend_jit_addr   __var_use_addr,
6337                                            zend_jit_addr   var_addr,
6338                                            uint32_t        __var_info,
6339                                            uint32_t        __var_def_info,
6340                                            zend_uchar      val_type,
6341                                            zend_jit_addr   val_addr,
6342                                            uint32_t        val_info,
6343                                            zend_jit_addr   __res_addr,
6344                                            bool       __check_exception)
6345{
6346	if (val_info & MAY_BE_UNDEF) {
6347		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
6348			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
6349			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6350
6351			if (!exit_addr) {
6352				return 0;
6353			}
6354
6355			|	IF_ZVAL_TYPE val_addr, IS_UNDEF, &exit_addr
6356		} else {
6357			|	IF_ZVAL_TYPE val_addr, IS_UNDEF, >1
6358			|.cold_code
6359			|1:
6360			ZEND_ASSERT(Z_REG(val_addr) == ZREG_FP);
6361			if (Z_REG(var_addr) != ZREG_FP) {
6362				|	mov aword T1, Ra(Z_REG(var_addr)) // save
6363			}
6364			|	SET_EX_OPLINE opline, r0
6365			|	mov FCARG1d, Z_OFFSET(val_addr)
6366			|	EXT_CALL zend_jit_undefined_op_helper, r0
6367			if (Z_REG(var_addr) != ZREG_FP) {
6368				|	mov Ra(Z_REG(var_addr)), aword T1 // restore
6369			}
6370			if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
6371				|	LOAD_ZVAL_ADDR FCARG1a, var_addr
6372			}
6373			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
6374			|	call ->assign_const
6375			|	jmp >9
6376			|.code
6377		}
6378	}
6379	if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
6380		|	LOAD_ZVAL_ADDR FCARG1a, var_addr
6381	}
6382	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
6383		|	LOAD_ZVAL_ADDR FCARG2a, val_addr
6384	}
6385	if (opline) {
6386		|	SET_EX_OPLINE opline, r0
6387	}
6388	if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
6389		|	call ->assign_tmp
6390	} else if (val_type == IS_CONST) {
6391		|	call ->assign_const
6392	} else if (val_type == IS_TMP_VAR) {
6393		|	call ->assign_tmp
6394	} else if (val_type == IS_VAR) {
6395		if (!(val_info & MAY_BE_REF)) {
6396			|	call ->assign_tmp
6397		} else {
6398			|	call ->assign_var
6399		}
6400	} else if (val_type == IS_CV) {
6401		if (!(val_info & MAY_BE_REF)) {
6402			|	call ->assign_cv_noref
6403		} else {
6404			|	call ->assign_cv
6405		}
6406		if ((val_info & MAY_BE_UNDEF) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
6407			|9:
6408		}
6409	} else {
6410		ZEND_UNREACHABLE();
6411	}
6412
6413	return 1;
6414}
6415
6416static int zend_jit_assign_to_variable(dasm_State    **Dst,
6417                                       const zend_op  *opline,
6418                                       zend_jit_addr   var_use_addr,
6419                                       zend_jit_addr   var_addr,
6420                                       uint32_t        var_info,
6421                                       uint32_t        var_def_info,
6422                                       zend_uchar      val_type,
6423                                       zend_jit_addr   val_addr,
6424                                       uint32_t        val_info,
6425                                       zend_jit_addr   res_addr,
6426                                       bool       check_exception)
6427/* Labels: 1,2,3,4,5,8 */
6428{
6429	int done = 0;
6430	zend_reg ref_reg, tmp_reg;
6431
6432	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_use_addr) != ZREG_R0) {
6433		ref_reg = ZREG_FCARG1;
6434		tmp_reg = ZREG_R0;
6435	} else {
6436		/* ASSIGN_DIM */
6437		ref_reg = ZREG_R0;
6438		tmp_reg = ZREG_FCARG1;
6439	}
6440
6441	if (var_info & MAY_BE_REF) {
6442		if (Z_MODE(var_use_addr) != IS_MEM_ZVAL || Z_REG(var_use_addr) != ref_reg || Z_OFFSET(var_use_addr) != 0) {
6443			|	LOAD_ZVAL_ADDR Ra(ref_reg), var_use_addr
6444			var_addr = var_use_addr = ZEND_ADDR_MEM_ZVAL(ref_reg, 0);
6445		}
6446		|	// if (Z_ISREF_P(variable_ptr)) {
6447		|	IF_NOT_Z_TYPE, Ra(ref_reg), IS_REFERENCE, >3
6448		|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
6449		|	GET_Z_PTR FCARG1a, Ra(ref_reg)
6450		if (!zend_jit_assign_to_typed_ref(Dst, opline, val_type, val_addr, res_addr, check_exception)) {
6451			return 0;
6452		}
6453		|	lea Ra(ref_reg), [FCARG1a + offsetof(zend_reference, val)]
6454		|3:
6455	}
6456	if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6457		if (RC_MAY_BE_1(var_info)) {
6458			int in_cold = 0;
6459
6460			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6461				|	IF_ZVAL_REFCOUNTED var_use_addr, >1
6462				|.cold_code
6463				|1:
6464				in_cold = 1;
6465			}
6466			if (Z_REG(var_use_addr) == ZREG_FCARG1 || Z_REG(var_use_addr) == ZREG_R0) {
6467				bool keep_gc = 0;
6468
6469				|	GET_ZVAL_PTR Ra(tmp_reg), var_use_addr
6470				if (tmp_reg == ZREG_FCARG1) {
6471					if (Z_MODE(val_addr) == IS_REG) {
6472						keep_gc = 1;
6473					} else if ((val_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) == 0) {
6474						keep_gc = 1;
6475					} else if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
6476						if (sizeof(void*) == 4) {
6477							keep_gc = 1;
6478						} else {
6479							zval *zv = Z_ZV(val_addr);
6480
6481							if (Z_TYPE_P(zv) == IS_DOUBLE) {
6482								if (Z_DVAL_P(zv) == 0 || IS_SIGNED_32BIT(zv)) {
6483									keep_gc = 1;
6484								}
6485							} else if (IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
6486								keep_gc = 1;
6487							}
6488						}
6489					} else if (Z_MODE(val_addr) == IS_MEM_ZVAL) {
6490						if ((val_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
6491							keep_gc = 1;
6492						}
6493					}
6494				}
6495				if (!keep_gc) {
6496					|	mov aword T1, Ra(tmp_reg) // save
6497				}
6498				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)) {
6499					return 0;
6500				}
6501				if (!keep_gc) {
6502					|	mov FCARG1a, aword T1 // restore
6503				}
6504			} else {
6505				|	GET_ZVAL_PTR FCARG1a, var_use_addr
6506				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)) {
6507					return 0;
6508				}
6509			}
6510			|	GC_DELREF FCARG1a
6511			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
6512				|	jnz >4
6513			} else {
6514				|	jnz >8
6515			}
6516			|	ZVAL_DTOR_FUNC var_info, opline
6517			if (in_cold || (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0)) {
6518				if (check_exception && !(val_info & MAY_BE_UNDEF)) {
6519					|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
6520					|	je >8
6521					|	jmp ->exception_handler
6522				} else {
6523					|	jmp >8
6524				}
6525			}
6526			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
6527				|4:
6528				|	IF_GC_MAY_NOT_LEAK FCARG1a, >8
6529				|	EXT_CALL gc_possible_root, r0
6530				if (in_cold) {
6531					|	jmp >8
6532				}
6533			}
6534			if (check_exception && (val_info & MAY_BE_UNDEF)) {
6535				|8:
6536				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
6537				|	je >8
6538				|	jmp ->exception_handler
6539			}
6540			if (in_cold) {
6541				|.code
6542			} else {
6543				done = 1;
6544			}
6545		} else /* if (RC_MAY_BE_N(var_info)) */ {
6546			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6547				|	IF_NOT_ZVAL_REFCOUNTED var_use_addr, >5
6548			}
6549			if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
6550				if (Z_REG(var_use_addr) != ZREG_FP) {
6551					|	mov T1, Ra(Z_REG(var_use_addr)) // save
6552				}
6553				|	GET_ZVAL_PTR FCARG1a, var_use_addr
6554				|	GC_DELREF FCARG1a
6555				|	IF_GC_MAY_NOT_LEAK FCARG1a, >5
6556				|	EXT_CALL gc_possible_root, r0
6557				if (Z_REG(var_use_addr) != ZREG_FP) {
6558					|	mov Ra(Z_REG(var_use_addr)), T1 // restore
6559				}
6560			} else {
6561				|	GET_ZVAL_PTR Ra(tmp_reg), var_use_addr
6562				|	GC_DELREF Ra(tmp_reg)
6563			}
6564			|5:
6565	    }
6566	}
6567
6568	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)) {
6569		return 0;
6570	}
6571
6572	|8:
6573
6574	return 1;
6575}
6576
6577static 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)
6578{
6579	zend_jit_addr op2_addr, op3_addr, res_addr;
6580
6581	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
6582	op3_addr = OP1_DATA_ADDR();
6583	if (opline->result_type == IS_UNUSED) {
6584		res_addr = 0;
6585	} else {
6586		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
6587	}
6588
6589	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (val_info & MAY_BE_UNDEF)) {
6590		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
6591		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6592
6593		if (!exit_addr) {
6594			return 0;
6595		}
6596
6597		|	IF_ZVAL_TYPE op3_addr, IS_UNDEF, &exit_addr
6598
6599		val_info &= ~MAY_BE_UNDEF;
6600	}
6601
6602	if (op1_info & MAY_BE_REF) {
6603		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
6604		|	IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
6605		|	GET_Z_PTR FCARG2a, FCARG1a
6606		|	IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
6607		|	lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
6608		|	jmp >3
6609		|.cold_code
6610		|2:
6611		|	SET_EX_OPLINE opline, r0
6612		|	EXT_CALL zend_jit_prepare_assign_dim_ref, r0
6613		|	test r0, r0
6614		|	mov FCARG1a, r0
6615		|	jne >1
6616		|	jmp ->exception_handler_undef
6617		|.code
6618		|1:
6619		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
6620	}
6621
6622	if (op1_info & MAY_BE_ARRAY) {
6623		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
6624			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
6625		}
6626		|3:
6627		|	SEPARATE_ARRAY op1_addr, op1_info, 1
6628	} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
6629		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6630			|	CMP_ZVAL_TYPE op1_addr, IS_NULL
6631			|	jg >7
6632		}
6633		|	// ZVAL_ARR(container, zend_new_array(8));
6634		if (Z_REG(op1_addr) != ZREG_FP) {
6635			|	mov T1, Ra(Z_REG(op1_addr)) // save
6636		}
6637		|	EXT_CALL _zend_new_array_0, r0
6638		if (Z_REG(op1_addr) != ZREG_FP) {
6639			|	mov Ra(Z_REG(op1_addr)), T1 // restore
6640		}
6641		|	SET_ZVAL_LVAL op1_addr, r0
6642		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
6643		|	mov FCARG1a, r0
6644	}
6645
6646	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6647		|6:
6648		if (opline->op2_type == IS_UNUSED) {
6649			uint32_t var_info = MAY_BE_NULL;
6650			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
6651
6652			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
6653			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
6654			|	EXT_CALL zend_hash_next_index_insert, r0
6655			|	// if (UNEXPECTED(!var_ptr)) {
6656			|	test r0, r0
6657			|	jz >1
6658			|.cold_code
6659			|1:
6660			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
6661			|	CANNOT_ADD_ELEMENT opline
6662			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
6663			|	jmp >9
6664			|.code
6665
6666			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)) {
6667				return 0;
6668			}
6669		} else {
6670			uint32_t var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
6671			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
6672
6673			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_W, op1_info, op2_info, dim_type, NULL, NULL, NULL)) {
6674				return 0;
6675			}
6676
6677			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
6678				var_info |= MAY_BE_REF;
6679			}
6680			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6681				var_info |= MAY_BE_RC1;
6682			}
6683
6684			|8:
6685			|	// value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE);
6686			if (opline->op1_type == IS_VAR) {
6687				ZEND_ASSERT(opline->result_type == IS_UNUSED);
6688				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)) {
6689					return 0;
6690				}
6691			} else {
6692				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)) {
6693					return 0;
6694				}
6695			}
6696		}
6697	}
6698
6699	if (((op1_info & MAY_BE_ARRAY) &&
6700	     (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) ||
6701	    (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY)))) {
6702		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6703			|.cold_code
6704			|7:
6705		}
6706
6707		if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) &&
6708		    (op1_info & MAY_BE_ARRAY)) {
6709			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6710				|	CMP_ZVAL_TYPE op1_addr, IS_NULL
6711				|	jg >2
6712			}
6713			|	// ZVAL_ARR(container, zend_new_array(8));
6714			if (Z_REG(op1_addr) != ZREG_FP) {
6715				|	mov T1, Ra(Z_REG(op1_addr)) // save
6716			}
6717			|	EXT_CALL _zend_new_array_0, r0
6718			if (Z_REG(op1_addr) != ZREG_FP) {
6719				|	mov Ra(Z_REG(op1_addr)), T1 // restore
6720			}
6721			|	SET_ZVAL_LVAL op1_addr, r0
6722			|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
6723			|	mov FCARG1a, r0
6724			|	// ZEND_VM_C_GOTO(assign_dim_op_new_array);
6725			|	jmp <6
6726			|2:
6727		}
6728
6729		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6730			|	SET_EX_OPLINE opline, r0
6731		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
6732				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
6733			}
6734		    if (opline->op2_type == IS_UNUSED) {
6735				|	xor FCARG2a, FCARG2a
6736			} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
6737				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
6738				|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
6739			} else {
6740				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
6741			}
6742			|.if not(X64)
6743			|	sub r4, 8
6744			|.endif
6745			if (opline->result_type == IS_UNUSED) {
6746				|.if X64
6747					|	xor CARG4, CARG4
6748				|.else
6749					|	push 0
6750				|.endif
6751			} else {
6752				|.if X64
6753					|	LOAD_ZVAL_ADDR CARG4, res_addr
6754				|.else
6755					|	PUSH_ZVAL_ADDR res_addr, r0
6756				|.endif
6757			}
6758			|.if X64
6759				|	LOAD_ZVAL_ADDR CARG3, op3_addr
6760			|.else
6761				|	PUSH_ZVAL_ADDR op3_addr, r0
6762			|.endif
6763			|	EXT_CALL zend_jit_assign_dim_helper, r0
6764			|.if not(X64)
6765			|	add r4, 8
6766			|.endif
6767
6768#ifdef ZEND_JIT_USE_RC_INFERENCE
6769			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR)) && (val_info & MAY_BE_RC1)) {
6770				/* ASSIGN_DIM may increase refcount of the value */
6771				val_info |= MAY_BE_RCN;
6772			}
6773#endif
6774
6775			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, NULL
6776		}
6777
6778		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6779			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6780				|	jmp >9 // END
6781			}
6782			|.code
6783		}
6784	}
6785
6786#ifdef ZEND_JIT_USE_RC_INFERENCE
6787	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))) {
6788		/* ASSIGN_DIM may increase refcount of the key */
6789		op2_info |= MAY_BE_RCN;
6790	}
6791#endif
6792
6793	|9:
6794	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
6795
6796	if (may_throw) {
6797		zend_jit_check_exception(Dst);
6798	}
6799
6800	return 1;
6801}
6802
6803static 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)
6804{
6805	zend_jit_addr op2_addr, op3_addr, var_addr;
6806	const void *not_found_exit_addr = NULL;
6807	uint32_t var_info = MAY_BE_NULL;
6808
6809	ZEND_ASSERT(opline->result_type == IS_UNUSED);
6810
6811	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
6812	op3_addr = OP1_DATA_ADDR();
6813
6814	|	SET_EX_OPLINE opline, r0
6815	if (op1_info & MAY_BE_REF) {
6816		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
6817		|	IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
6818		|	GET_Z_PTR FCARG2a, FCARG1a
6819		|	IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
6820		|	lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
6821		|	jmp >3
6822		|.cold_code
6823		|2:
6824		|	EXT_CALL zend_jit_prepare_assign_dim_ref, r0
6825		|	test r0, r0
6826		|	mov FCARG1a, r0
6827		|	jne >1
6828		|	jmp ->exception_handler_undef
6829		|.code
6830		|1:
6831		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
6832	}
6833
6834	if (op1_info & MAY_BE_ARRAY) {
6835		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
6836			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
6837		}
6838		|3:
6839		|	SEPARATE_ARRAY op1_addr, op1_info, 1
6840	}
6841	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
6842		if (op1_info & MAY_BE_ARRAY) {
6843			|.cold_code
6844			|7:
6845		}
6846		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6847			|	CMP_ZVAL_TYPE op1_addr, IS_NULL
6848			|	jg >7
6849		}
6850		if (Z_REG(op1_addr) != ZREG_FP) {
6851			|	mov T1, Ra(Z_REG(op1_addr)) // save
6852		}
6853		if (op1_info & MAY_BE_UNDEF) {
6854			if (op1_info & MAY_BE_NULL) {
6855				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
6856			}
6857			|	mov FCARG1a, opline->op1.var
6858			|	EXT_CALL zend_jit_undefined_op_helper, r0
6859			|1:
6860		}
6861		|	// ZVAL_ARR(container, zend_new_array(8));
6862		|	EXT_CALL _zend_new_array_0, r0
6863		if (Z_REG(op1_addr) != ZREG_FP) {
6864			|	mov Ra(Z_REG(op1_addr)), T1 // restore
6865		}
6866		|	SET_ZVAL_LVAL op1_addr, r0
6867		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
6868		|	mov FCARG1a, r0
6869		if (op1_info & MAY_BE_ARRAY) {
6870			|	jmp >1
6871			|.code
6872			|1:
6873		}
6874	}
6875
6876	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6877		uint32_t var_def_info = zend_array_element_type(op1_def_info, opline->op1_type, 1, 0);
6878
6879		|6:
6880		if (opline->op2_type == IS_UNUSED) {
6881			var_info = MAY_BE_NULL;
6882
6883			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
6884			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
6885			|	EXT_CALL zend_hash_next_index_insert, r0
6886			|	// if (UNEXPECTED(!var_ptr)) {
6887			|	test r0, r0
6888			|	jz >1
6889			|.cold_code
6890			|1:
6891			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
6892			|	CANNOT_ADD_ELEMENT opline
6893			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
6894			|	jmp >9
6895			|.code
6896		} else {
6897			var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
6898			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
6899				var_info |= MAY_BE_REF;
6900			}
6901			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6902				var_info |= MAY_BE_RC1;
6903			}
6904
6905			if (dim_type != IS_UNKNOWN
6906			 && dim_type != IS_UNDEF
6907			 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY
6908			 && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))
6909			 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
6910				int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
6911				not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6912				if (!not_found_exit_addr) {
6913					return 0;
6914				}
6915			}
6916
6917			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, dim_type, NULL, not_found_exit_addr, NULL)) {
6918				return 0;
6919			}
6920
6921			|8:
6922			if (not_found_exit_addr && dim_type != IS_REFERENCE) {
6923				|	IF_NOT_Z_TYPE, r0, dim_type, &not_found_exit_addr
6924				var_info = (1 << dim_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
6925			}
6926			if (var_info & MAY_BE_REF) {
6927				binary_op_type binary_op = get_binary_op(opline->extended_value);
6928				|	IF_NOT_Z_TYPE, r0, IS_REFERENCE, >1
6929				|	GET_Z_PTR FCARG1a, r0
6930				|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
6931				|	jnz >2
6932				|	lea r0, aword [FCARG1a + offsetof(zend_reference, val)]
6933				|.cold_code
6934				|2:
6935				|	LOAD_ZVAL_ADDR FCARG2a, op3_addr
6936				|.if X64
6937					|	LOAD_ADDR CARG3, binary_op
6938				|.else
6939					|	sub r4, 12
6940					|	PUSH_ADDR binary_op, r0
6941				|.endif
6942				if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
6943				 && (op1_data_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6944					|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
6945				} else {
6946					|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
6947				}
6948				|.if not(X64)
6949				|	add r4, 12
6950				|.endif
6951				|	jmp >9
6952				|.code
6953				|1:
6954			}
6955		}
6956
6957		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
6958		switch (opline->extended_value) {
6959			case ZEND_ADD:
6960			case ZEND_SUB:
6961			case ZEND_MUL:
6962			case ZEND_DIV:
6963				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,
6964						1 /* may overflow */, may_throw)) {
6965					return 0;
6966				}
6967				break;
6968			case ZEND_BW_OR:
6969			case ZEND_BW_AND:
6970			case ZEND_BW_XOR:
6971			case ZEND_SL:
6972			case ZEND_SR:
6973			case ZEND_MOD:
6974				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
6975						IS_CV, opline->op1, var_addr, var_info, NULL,
6976						(opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info,
6977						op1_data_range,
6978						0, var_addr, var_def_info, var_info, may_throw)) {
6979					return 0;
6980				}
6981				break;
6982			case ZEND_CONCAT:
6983				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,
6984						may_throw)) {
6985					return 0;
6986				}
6987				break;
6988			default:
6989				ZEND_UNREACHABLE();
6990		}
6991		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
6992	}
6993
6994	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6995		binary_op_type binary_op;
6996
6997		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6998			|.cold_code
6999			|7:
7000		}
7001
7002		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
7003			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
7004		}
7005	    if (opline->op2_type == IS_UNUSED) {
7006			|	xor FCARG2a, FCARG2a
7007		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
7008			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
7009			|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
7010		} else {
7011			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
7012		}
7013		binary_op = get_binary_op(opline->extended_value);
7014		|.if X64
7015			|	LOAD_ZVAL_ADDR CARG3, op3_addr
7016			|	LOAD_ADDR CARG4, binary_op
7017		|.else
7018			|	sub r4, 8
7019			|	PUSH_ADDR binary_op, r0
7020			|	PUSH_ZVAL_ADDR op3_addr, r0
7021		|.endif
7022		|	EXT_CALL zend_jit_assign_dim_op_helper, r0
7023		|.if not(X64)
7024		|	add r4, 8
7025		|.endif
7026
7027		|9:
7028		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, NULL
7029		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL
7030		if (may_throw) {
7031			zend_jit_check_exception(Dst);
7032		}
7033
7034		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
7035			|	jmp >9 // END
7036			|.code
7037			|9:
7038		}
7039	} else if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY))
7040			&& (!not_found_exit_addr || (var_info & MAY_BE_REF))) {
7041		|.cold_code
7042		|9:
7043		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, opline
7044		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
7045		if (may_throw) {
7046			zend_jit_check_exception(Dst);
7047		}
7048		|	jmp >9
7049		|.code
7050		|9:
7051	}
7052
7053	return 1;
7054}
7055
7056static 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)
7057{
7058	zend_jit_addr op1_addr, op2_addr;
7059
7060	ZEND_ASSERT(opline->op1_type == IS_CV && opline->result_type == IS_UNUSED);
7061	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
7062
7063	op1_addr = OP1_ADDR();
7064	op2_addr = OP2_ADDR();
7065
7066	if (op1_info & MAY_BE_REF) {
7067		binary_op_type binary_op = get_binary_op(opline->extended_value);
7068		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
7069		|	IF_NOT_Z_TYPE, FCARG1a, IS_REFERENCE, >1
7070		|	GET_Z_PTR FCARG1a, FCARG1a
7071		|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
7072		|	jnz >2
7073		|	add FCARG1a, offsetof(zend_reference, val)
7074		|.cold_code
7075		|2:
7076		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
7077		|.if X64
7078			|	LOAD_ADDR CARG3, binary_op
7079		|.else
7080			|	sub r4, 12
7081			|	PUSH_ADDR binary_op, r0
7082		|.endif
7083		|	SET_EX_OPLINE opline, r0
7084		if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
7085		 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
7086			|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
7087		} else {
7088			|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
7089		}
7090		|.if not(X64)
7091		|	add r4, 12
7092		|.endif
7093		zend_jit_check_exception(Dst);
7094		|	jmp >9
7095		|.code
7096		|1:
7097		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
7098	}
7099
7100	int result;
7101	switch (opline->extended_value) {
7102		case ZEND_ADD:
7103		case ZEND_SUB:
7104		case ZEND_MUL:
7105		case ZEND_DIV:
7106			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);
7107			break;
7108		case ZEND_BW_OR:
7109		case ZEND_BW_AND:
7110		case ZEND_BW_XOR:
7111		case ZEND_SL:
7112		case ZEND_SR:
7113		case ZEND_MOD:
7114			result = zend_jit_long_math_helper(Dst, opline, opline->extended_value,
7115				opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
7116				opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
7117				opline->op1.var, op1_addr, op1_def_info, op1_info, may_throw);
7118			break;
7119		case ZEND_CONCAT:
7120			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);
7121			break;
7122		default:
7123			ZEND_UNREACHABLE();
7124	}
7125	|9:
7126	return result;
7127}
7128
7129static int zend_jit_cmp_long_long(dasm_State    **Dst,
7130                                  const zend_op  *opline,
7131                                  zend_ssa_range *op1_range,
7132                                  zend_jit_addr   op1_addr,
7133                                  zend_ssa_range *op2_range,
7134                                  zend_jit_addr   op2_addr,
7135                                  zend_jit_addr   res_addr,
7136                                  zend_uchar      smart_branch_opcode,
7137                                  uint32_t        target_label,
7138                                  uint32_t        target_label2,
7139                                  const void     *exit_addr,
7140                                  bool       skip_comparison)
7141{
7142	bool swap = 0;
7143	bool result;
7144
7145	if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) {
7146		if (!smart_branch_opcode ||
7147		    smart_branch_opcode == ZEND_JMPZ_EX ||
7148		    smart_branch_opcode == ZEND_JMPNZ_EX) {
7149			|	SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE)
7150		}
7151		if (smart_branch_opcode && !exit_addr) {
7152			if (smart_branch_opcode == ZEND_JMPZ ||
7153			    smart_branch_opcode == ZEND_JMPZ_EX) {
7154				if (!result) {
7155					| jmp => target_label
7156				}
7157			} else if (smart_branch_opcode == ZEND_JMPNZ ||
7158			           smart_branch_opcode == ZEND_JMPNZ_EX) {
7159				if (result) {
7160					| jmp => target_label
7161				}
7162			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
7163				if (!result) {
7164					| jmp => target_label
7165				} else {
7166					| jmp => target_label2
7167				}
7168			} else {
7169				ZEND_UNREACHABLE();
7170			}
7171		}
7172		return 1;
7173	}
7174
7175	if (skip_comparison) {
7176		if (Z_MODE(op1_addr) != IS_REG &&
7177		    (Z_MODE(op2_addr) == IS_REG ||
7178		     (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL))) {
7179			swap = 1;
7180		}
7181	} else if (Z_MODE(op1_addr) == IS_REG) {
7182		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
7183			|	test Ra(Z_REG(op1_addr)), Ra(Z_REG(op1_addr))
7184		} else {
7185			|	LONG_OP cmp, Z_REG(op1_addr), op2_addr, r0
7186		}
7187	} else if (Z_MODE(op2_addr) == IS_REG) {
7188		if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 0) {
7189			|	test Ra(Z_REG(op2_addr)), Ra(Z_REG(op2_addr))
7190		} else {
7191			|	LONG_OP cmp, Z_REG(op2_addr), op1_addr, r0
7192		}
7193		swap = 1;
7194	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL) {
7195		|	LONG_OP_WITH_CONST cmp, op2_addr, Z_LVAL_P(Z_ZV(op1_addr))
7196		swap = 1;
7197	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_MODE(op1_addr) != IS_CONST_ZVAL) {
7198		|	LONG_OP_WITH_CONST cmp, op1_addr, Z_LVAL_P(Z_ZV(op2_addr))
7199	} else {
7200		|	GET_ZVAL_LVAL ZREG_R0, op1_addr
7201		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
7202			|	test r0, r0
7203		} else {
7204			|	LONG_OP cmp, ZREG_R0, op2_addr, r0
7205		}
7206	}
7207
7208	if (smart_branch_opcode) {
7209		if (smart_branch_opcode == ZEND_JMPZ_EX ||
7210		    smart_branch_opcode == ZEND_JMPNZ_EX) {
7211
7212			switch (opline->opcode) {
7213				case ZEND_IS_EQUAL:
7214				case ZEND_IS_IDENTICAL:
7215				case ZEND_CASE:
7216				case ZEND_CASE_STRICT:
7217					|	sete al
7218					break;
7219				case ZEND_IS_NOT_EQUAL:
7220				case ZEND_IS_NOT_IDENTICAL:
7221					|	setne al
7222					break;
7223				case ZEND_IS_SMALLER:
7224					if (swap) {
7225						|	setg al
7226					} else {
7227						|	setl al
7228					}
7229					break;
7230				case ZEND_IS_SMALLER_OR_EQUAL:
7231					if (swap) {
7232						|	setge al
7233					} else {
7234						|	setle al
7235					}
7236					break;
7237				default:
7238					ZEND_UNREACHABLE();
7239			}
7240			|	movzx eax, al
7241			|	lea eax, [eax + 2]
7242			|	SET_ZVAL_TYPE_INFO res_addr, eax
7243		}
7244		if (smart_branch_opcode == ZEND_JMPZ ||
7245		    smart_branch_opcode == ZEND_JMPZ_EX) {
7246			switch (opline->opcode) {
7247				case ZEND_IS_EQUAL:
7248				case ZEND_IS_IDENTICAL:
7249				case ZEND_CASE:
7250				case ZEND_CASE_STRICT:
7251					if (exit_addr) {
7252						| jne &exit_addr
7253					} else {
7254						| jne => target_label
7255					}
7256					break;
7257				case ZEND_IS_NOT_EQUAL:
7258					if (exit_addr) {
7259						| je &exit_addr
7260					} else {
7261						| je => target_label
7262					}
7263					break;
7264				case ZEND_IS_NOT_IDENTICAL:
7265					if (exit_addr) {
7266						| jne &exit_addr
7267					} else {
7268						| je => target_label
7269					}
7270					break;
7271				case ZEND_IS_SMALLER:
7272					if (swap) {
7273						if (exit_addr) {
7274							| jle &exit_addr
7275						} else {
7276							| jle => target_label
7277						}
7278					} else {
7279						if (exit_addr) {
7280							| jge &exit_addr
7281						} else {
7282							| jge => target_label
7283						}
7284					}
7285					break;
7286				case ZEND_IS_SMALLER_OR_EQUAL:
7287					if (swap) {
7288						if (exit_addr) {
7289							| jl &exit_addr
7290						} else {
7291							| jl => target_label
7292						}
7293					} else {
7294						if (exit_addr) {
7295							| jg &exit_addr
7296						} else {
7297							| jg => target_label
7298						}
7299					}
7300					break;
7301				default:
7302					ZEND_UNREACHABLE();
7303			}
7304		} else if (smart_branch_opcode == ZEND_JMPNZ ||
7305		           smart_branch_opcode == ZEND_JMPNZ_EX) {
7306			switch (opline->opcode) {
7307				case ZEND_IS_EQUAL:
7308				case ZEND_IS_IDENTICAL:
7309				case ZEND_CASE:
7310				case ZEND_CASE_STRICT:
7311					if (exit_addr) {
7312						| je &exit_addr
7313					} else {
7314						| je => target_label
7315					}
7316					break;
7317				case ZEND_IS_NOT_EQUAL:
7318					if (exit_addr) {
7319						| jne &exit_addr
7320					} else {
7321						| jne => target_label
7322					}
7323					break;
7324				case ZEND_IS_NOT_IDENTICAL:
7325					if (exit_addr) {
7326						| je &exit_addr
7327					} else {
7328						| jne => target_label
7329					}
7330					break;
7331				case ZEND_IS_SMALLER:
7332					if (swap) {
7333						if (exit_addr) {
7334							| jg &exit_addr
7335						} else {
7336							| jg => target_label
7337						}
7338					} else {
7339						if (exit_addr) {
7340							| jl &exit_addr
7341						} else {
7342							| jl => target_label
7343						}
7344					}
7345					break;
7346				case ZEND_IS_SMALLER_OR_EQUAL:
7347					if (swap) {
7348						if (exit_addr) {
7349							| jge &exit_addr
7350						} else {
7351							| jge => target_label
7352						}
7353					} else {
7354						if (exit_addr) {
7355							| jle &exit_addr
7356						} else {
7357							| jle => target_label
7358						}
7359					}
7360					break;
7361				default:
7362					ZEND_UNREACHABLE();
7363			}
7364		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
7365			switch (opline->opcode) {
7366				case ZEND_IS_EQUAL:
7367				case ZEND_IS_IDENTICAL:
7368				case ZEND_CASE:
7369				case ZEND_CASE_STRICT:
7370					| jne => target_label
7371					break;
7372				case ZEND_IS_NOT_EQUAL:
7373				case ZEND_IS_NOT_IDENTICAL:
7374					| je => target_label
7375					break;
7376				case ZEND_IS_SMALLER:
7377				    if (swap) {
7378						| jle => target_label
7379				    } else {
7380						| jge => target_label
7381					}
7382					break;
7383				case ZEND_IS_SMALLER_OR_EQUAL:
7384					if (swap) {
7385						| jl => target_label
7386					} else {
7387						| jg => target_label
7388					}
7389					break;
7390				default:
7391					ZEND_UNREACHABLE();
7392			}
7393			| jmp => target_label2
7394		} else {
7395			ZEND_UNREACHABLE();
7396		}
7397	} else {
7398		switch (opline->opcode) {
7399			case ZEND_IS_EQUAL:
7400			case ZEND_IS_IDENTICAL:
7401			case ZEND_CASE:
7402			case ZEND_CASE_STRICT:
7403				|	sete al
7404				break;
7405			case ZEND_IS_NOT_EQUAL:
7406			case ZEND_IS_NOT_IDENTICAL:
7407				|	setne al
7408				break;
7409			case ZEND_IS_SMALLER:
7410				if (swap) {
7411					|	setg al
7412				} else {
7413					|	setl al
7414				}
7415				break;
7416			case ZEND_IS_SMALLER_OR_EQUAL:
7417				if (swap) {
7418					|	setge al
7419				} else {
7420					|	setle al
7421				}
7422				break;
7423			default:
7424				ZEND_UNREACHABLE();
7425		}
7426		|	movzx eax, al
7427		|	add eax, 2
7428		|	SET_ZVAL_TYPE_INFO res_addr, eax
7429	}
7430
7431	return 1;
7432}
7433
7434static int zend_jit_cmp_double_common(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, bool swap, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7435{
7436	if (smart_branch_opcode) {
7437		if (smart_branch_opcode == ZEND_JMPZ) {
7438			switch (opline->opcode) {
7439				case ZEND_IS_EQUAL:
7440				case ZEND_IS_IDENTICAL:
7441				case ZEND_CASE:
7442				case ZEND_CASE_STRICT:
7443					if (exit_addr) {
7444						| jne &exit_addr
7445						| jp &exit_addr
7446					} else {
7447						| jne => target_label
7448						| jp => target_label
7449					}
7450					break;
7451				case ZEND_IS_NOT_EQUAL:
7452					| jp >1
7453					if (exit_addr) {
7454						| je &exit_addr
7455					} else {
7456						| je => target_label
7457					}
7458					|1:
7459					break;
7460				case ZEND_IS_NOT_IDENTICAL:
7461					if (exit_addr) {
7462						| jne &exit_addr
7463						| jp &exit_addr
7464					} else {
7465						| jp >1
7466						| je => target_label
7467						|1:
7468					}
7469					break;
7470				case ZEND_IS_SMALLER:
7471					if (swap) {
7472						if (exit_addr) {
7473							| jbe &exit_addr
7474						} else {
7475							| jbe => target_label
7476						}
7477					} else {
7478						if (exit_addr) {
7479							| jae &exit_addr
7480							| jp &exit_addr
7481						} else {
7482							| jae => target_label
7483							| jp => target_label
7484						}
7485					}
7486					break;
7487				case ZEND_IS_SMALLER_OR_EQUAL:
7488					if (swap) {
7489						if (exit_addr) {
7490							| jb &exit_addr
7491						} else {
7492							| jb => target_label
7493						}
7494					} else {
7495						if (exit_addr) {
7496							| ja &exit_addr
7497							| jp &exit_addr
7498						} else {
7499							| ja => target_label
7500							| jp => target_label
7501						}
7502					}
7503					break;
7504				default:
7505					ZEND_UNREACHABLE();
7506			}
7507		} else if (smart_branch_opcode == ZEND_JMPNZ) {
7508			switch (opline->opcode) {
7509				case ZEND_IS_EQUAL:
7510				case ZEND_IS_IDENTICAL:
7511				case ZEND_CASE:
7512				case ZEND_CASE_STRICT:
7513					| jp >1
7514					if (exit_addr) {
7515						| je &exit_addr
7516					} else {
7517						| je => target_label
7518					}
7519					|1:
7520					break;
7521				case ZEND_IS_NOT_EQUAL:
7522					if (exit_addr) {
7523						| jne &exit_addr
7524						| jp &exit_addr
7525					} else {
7526						| jne => target_label
7527						| jp => target_label
7528					}
7529					break;
7530				case ZEND_IS_NOT_IDENTICAL:
7531					if (exit_addr) {
7532						| jp >1
7533						| je &exit_addr
7534						|1:
7535					} else {
7536						| jne => target_label
7537						| jp => target_label
7538					}
7539					break;
7540				case ZEND_IS_SMALLER:
7541					if (swap) {
7542						if (exit_addr) {
7543							| ja &exit_addr
7544						} else {
7545							| ja => target_label
7546						}
7547					} else {
7548						| jp >1
7549						if (exit_addr) {
7550							| jb &exit_addr
7551						} else {
7552							| jb => target_label
7553						}
7554						|1:
7555					}
7556					break;
7557				case ZEND_IS_SMALLER_OR_EQUAL:
7558					if (swap) {
7559						if (exit_addr) {
7560							| jae &exit_addr
7561						} else {
7562							| jae => target_label
7563						}
7564					} else {
7565						| jp >1
7566						if (exit_addr) {
7567							| jbe &exit_addr
7568						} else {
7569							| jbe => target_label
7570						}
7571						|1:
7572					}
7573					break;
7574				default:
7575					ZEND_UNREACHABLE();
7576			}
7577		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
7578			switch (opline->opcode) {
7579				case ZEND_IS_EQUAL:
7580				case ZEND_IS_IDENTICAL:
7581				case ZEND_CASE:
7582				case ZEND_CASE_STRICT:
7583					| jne => target_label
7584					| jp => target_label
7585					break;
7586				case ZEND_IS_NOT_EQUAL:
7587				case ZEND_IS_NOT_IDENTICAL:
7588					| jp => target_label2
7589					| je => target_label
7590					break;
7591				case ZEND_IS_SMALLER:
7592					if (swap) {
7593						| jbe => target_label
7594					} else {
7595						| jae => target_label
7596						| jp => target_label
7597					}
7598					break;
7599				case ZEND_IS_SMALLER_OR_EQUAL:
7600					if (swap) {
7601						| jb => target_label
7602					} else {
7603						| ja => target_label
7604						| jp => target_label
7605					}
7606					break;
7607				default:
7608					ZEND_UNREACHABLE();
7609			}
7610			| jmp => target_label2
7611		} else if (smart_branch_opcode == ZEND_JMPZ_EX) {
7612			switch (opline->opcode) {
7613				case ZEND_IS_EQUAL:
7614				case ZEND_IS_IDENTICAL:
7615				case ZEND_CASE:
7616				case ZEND_CASE_STRICT:
7617					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7618					|	jne => target_label
7619					|	jp => target_label
7620					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7621					break;
7622				case ZEND_IS_NOT_EQUAL:
7623				case ZEND_IS_NOT_IDENTICAL:
7624					|	jp >1
7625					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7626					|	je => target_label
7627					|1:
7628					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7629					break;
7630				case ZEND_IS_SMALLER:
7631					if (swap) {
7632						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7633						|	jbe => target_label
7634						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7635					} else {
7636						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7637						|	jae => target_label
7638						|	jp => target_label
7639						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7640					}
7641					break;
7642				case ZEND_IS_SMALLER_OR_EQUAL:
7643					if (swap) {
7644						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7645						|	jb => target_label
7646						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7647					} else {
7648						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7649						|	ja => target_label
7650						|	jp => target_label
7651						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7652					}
7653					break;
7654				default:
7655					ZEND_UNREACHABLE();
7656			}
7657		} else if (smart_branch_opcode == ZEND_JMPNZ_EX) {
7658			switch (opline->opcode) {
7659				case ZEND_IS_EQUAL:
7660				case ZEND_IS_IDENTICAL:
7661				case ZEND_CASE:
7662				case ZEND_CASE_STRICT:
7663					|	jp >1
7664					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7665					|	je => target_label
7666					|1:
7667					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7668					break;
7669				case ZEND_IS_NOT_EQUAL:
7670				case ZEND_IS_NOT_IDENTICAL:
7671					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7672					|	jne => target_label
7673					|	jp => target_label
7674					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7675					break;
7676				case ZEND_IS_SMALLER:
7677					if (swap) {
7678						|	seta al
7679						|	movzx eax, al
7680						|	lea eax, [eax + 2]
7681						|	SET_ZVAL_TYPE_INFO res_addr, eax
7682						|	ja => target_label
7683					} else {
7684						|	jp >1
7685						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7686						|	jb => target_label
7687						|1:
7688						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7689					}
7690					break;
7691				case ZEND_IS_SMALLER_OR_EQUAL:
7692					if (swap) {
7693						|	setae al
7694						|	movzx eax, al
7695						|	lea eax, [eax + 2]
7696						|	SET_ZVAL_TYPE_INFO res_addr, eax
7697						|	jae => target_label
7698					} else {
7699						|	jp >1
7700						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
7701						|	jbe => target_label
7702						|1:
7703						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
7704					}
7705					break;
7706				default:
7707					ZEND_UNREACHABLE();
7708			}
7709		} else {
7710			ZEND_UNREACHABLE();
7711		}
7712	} else {
7713		switch (opline->opcode) {
7714			case ZEND_IS_EQUAL:
7715			case ZEND_IS_IDENTICAL:
7716			case ZEND_CASE:
7717			case ZEND_CASE_STRICT:
7718				|	jp >1
7719				|	mov eax, IS_TRUE
7720				|	je >2
7721				|1:
7722				|	mov eax, IS_FALSE
7723				|2:
7724				break;
7725			case ZEND_IS_NOT_EQUAL:
7726			case ZEND_IS_NOT_IDENTICAL:
7727				|	jp >1
7728				|	mov eax, IS_FALSE
7729				|	je >2
7730				|1:
7731				|	mov eax, IS_TRUE
7732				|2:
7733				break;
7734			case ZEND_IS_SMALLER:
7735				if (swap) {
7736					|	seta al
7737					|	movzx eax, al
7738					|	add eax, 2
7739				} else {
7740					|	jp >1
7741					|	mov eax, IS_TRUE
7742					|	jb >2
7743					|1:
7744					|	mov eax, IS_FALSE
7745					|2:
7746				}
7747				break;
7748			case ZEND_IS_SMALLER_OR_EQUAL:
7749				if (swap) {
7750					|	setae al
7751					|	movzx eax, al
7752					|	add eax, 2
7753				} else {
7754					|	jp >1
7755					|	mov eax, IS_TRUE
7756					|	jbe >2
7757					|1:
7758					|	mov eax, IS_FALSE
7759					|2:
7760				}
7761				break;
7762			default:
7763				ZEND_UNREACHABLE();
7764		}
7765		|	SET_ZVAL_TYPE_INFO res_addr, eax
7766	}
7767
7768	return 1;
7769}
7770
7771static int zend_jit_cmp_long_double(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7772{
7773	zend_reg tmp_reg = ZREG_XMM0;
7774
7775	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op1_addr, ZREG_R0
7776	|	DOUBLE_CMP tmp_reg, op2_addr
7777
7778	return zend_jit_cmp_double_common(Dst, opline, res_addr, 0, smart_branch_opcode, target_label, target_label2, exit_addr);
7779}
7780
7781static int zend_jit_cmp_double_long(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7782{
7783	zend_reg tmp_reg = ZREG_XMM0;
7784
7785	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, ZREG_R0
7786	|	DOUBLE_CMP tmp_reg, op1_addr
7787
7788	return zend_jit_cmp_double_common(Dst, opline, res_addr, /* swap */ 1, smart_branch_opcode, target_label, target_label2, exit_addr);
7789}
7790
7791static int zend_jit_cmp_double_double(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7792{
7793	bool swap = 0;
7794
7795	if (Z_MODE(op1_addr) == IS_REG) {
7796		|	DOUBLE_CMP Z_REG(op1_addr), op2_addr
7797	} else if (Z_MODE(op2_addr) == IS_REG) {
7798		|	DOUBLE_CMP Z_REG(op2_addr), op1_addr
7799		swap = 1;
7800	} else {
7801		zend_reg tmp_reg = ZREG_XMM0;
7802
7803		|	DOUBLE_GET_ZVAL_DVAL tmp_reg, op1_addr
7804		|	DOUBLE_CMP tmp_reg, op2_addr
7805	}
7806
7807	return zend_jit_cmp_double_common(Dst, opline, res_addr, swap, smart_branch_opcode, target_label, target_label2, exit_addr);
7808}
7809
7810static int zend_jit_cmp_slow(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
7811{
7812	|	test, eax, eax
7813	if (smart_branch_opcode) {
7814		if (smart_branch_opcode == ZEND_JMPZ_EX ||
7815		    smart_branch_opcode == ZEND_JMPNZ_EX) {
7816			switch (opline->opcode) {
7817				case ZEND_IS_EQUAL:
7818				case ZEND_CASE:
7819					|	sete al
7820					break;
7821				case ZEND_IS_NOT_EQUAL:
7822					|	setne al
7823					break;
7824				case ZEND_IS_SMALLER:
7825					|	setl al
7826					break;
7827				case ZEND_IS_SMALLER_OR_EQUAL:
7828					|	setle al
7829					break;
7830				default:
7831					ZEND_UNREACHABLE();
7832			}
7833			|	movzx eax, al
7834			|	lea eax, [eax + 2]
7835			|	SET_ZVAL_TYPE_INFO res_addr, eax
7836		}
7837		if (smart_branch_opcode == ZEND_JMPZ ||
7838		    smart_branch_opcode == ZEND_JMPZ_EX) {
7839			switch (opline->opcode) {
7840				case ZEND_IS_EQUAL:
7841				case ZEND_CASE:
7842					if (exit_addr) {
7843						| jne &exit_addr
7844					} else {
7845						| jne => target_label
7846					}
7847					break;
7848				case ZEND_IS_NOT_EQUAL:
7849					if (exit_addr) {
7850						| je &exit_addr
7851					} else {
7852						| je => target_label
7853					}
7854					break;
7855				case ZEND_IS_SMALLER:
7856					if (exit_addr) {
7857						| jge &exit_addr
7858					} else {
7859						| jge => target_label
7860					}
7861					break;
7862				case ZEND_IS_SMALLER_OR_EQUAL:
7863					if (exit_addr) {
7864						| jg &exit_addr
7865					} else {
7866						| jg => target_label
7867					}
7868					break;
7869				default:
7870					ZEND_UNREACHABLE();
7871			}
7872		} else if (smart_branch_opcode == ZEND_JMPNZ ||
7873		           smart_branch_opcode == ZEND_JMPNZ_EX) {
7874			switch (opline->opcode) {
7875				case ZEND_IS_EQUAL:
7876				case ZEND_CASE:
7877					if (exit_addr) {
7878						| je &exit_addr
7879					} else {
7880						| je => target_label
7881					}
7882					break;
7883				case ZEND_IS_NOT_EQUAL:
7884					if (exit_addr) {
7885						| jne &exit_addr
7886					} else {
7887						| jne => target_label
7888					}
7889					break;
7890				case ZEND_IS_SMALLER:
7891					if (exit_addr) {
7892						| jl &exit_addr
7893					} else {
7894						| jl => target_label
7895					}
7896					break;
7897				case ZEND_IS_SMALLER_OR_EQUAL:
7898					if (exit_addr) {
7899						| jle &exit_addr
7900					} else {
7901						| jle => target_label
7902					}
7903					break;
7904				default:
7905					ZEND_UNREACHABLE();
7906			}
7907		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
7908			switch (opline->opcode) {
7909				case ZEND_IS_EQUAL:
7910				case ZEND_CASE:
7911					| jne => target_label
7912					break;
7913				case ZEND_IS_NOT_EQUAL:
7914					| je => target_label
7915					break;
7916				case ZEND_IS_SMALLER:
7917					| jge => target_label
7918					break;
7919				case ZEND_IS_SMALLER_OR_EQUAL:
7920					| jg => target_label
7921					break;
7922				default:
7923					ZEND_UNREACHABLE();
7924			}
7925			| jmp => target_label2
7926		} else {
7927			ZEND_UNREACHABLE();
7928		}
7929	} else {
7930		switch (opline->opcode) {
7931			case ZEND_IS_EQUAL:
7932			case ZEND_CASE:
7933				|	sete al
7934				break;
7935			case ZEND_IS_NOT_EQUAL:
7936				|	setne al
7937				break;
7938			case ZEND_IS_SMALLER:
7939				|	setl al
7940				break;
7941			case ZEND_IS_SMALLER_OR_EQUAL:
7942				|	setle al
7943				break;
7944			default:
7945				ZEND_UNREACHABLE();
7946		}
7947		|	movzx eax, al
7948		|	add eax, 2
7949		|	SET_ZVAL_TYPE_INFO res_addr, eax
7950	}
7951
7952	return 1;
7953}
7954
7955static int zend_jit_cmp(dasm_State    **Dst,
7956                        const zend_op  *opline,
7957                        uint32_t        op1_info,
7958                        zend_ssa_range *op1_range,
7959                        zend_jit_addr   op1_addr,
7960                        uint32_t        op2_info,
7961                        zend_ssa_range *op2_range,
7962                        zend_jit_addr   op2_addr,
7963                        zend_jit_addr   res_addr,
7964                        int             may_throw,
7965                        zend_uchar      smart_branch_opcode,
7966                        uint32_t        target_label,
7967                        uint32_t        target_label2,
7968                        const void     *exit_addr,
7969                        bool       skip_comparison)
7970{
7971	bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
7972	bool has_slow;
7973
7974	has_slow =
7975		(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
7976		(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
7977		((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
7978		 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))));
7979
7980	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
7981		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
7982			if (op1_info & MAY_BE_DOUBLE) {
7983				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >4
7984			} else {
7985				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9
7986			}
7987		}
7988		if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
7989			if (op2_info & MAY_BE_DOUBLE) {
7990				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3
7991				|.cold_code
7992				|3:
7993				if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
7994					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
7995				}
7996				if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7997					return 0;
7998				}
7999				|	jmp >6
8000				|.code
8001			} else {
8002				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
8003			}
8004		}
8005		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)) {
8006			return 0;
8007		}
8008		if (op1_info & MAY_BE_DOUBLE) {
8009			|.cold_code
8010			|4:
8011			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
8012				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9
8013			}
8014			if (op2_info & MAY_BE_DOUBLE) {
8015				if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
8016					if (!same_ops) {
8017						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >5
8018					} else {
8019						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
8020					}
8021				}
8022				if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8023					return 0;
8024				}
8025				|	jmp >6
8026			}
8027			if (!same_ops) {
8028				|5:
8029				if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
8030					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
8031				}
8032				if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8033					return 0;
8034				}
8035				|	jmp >6
8036			}
8037			|.code
8038		}
8039	} else if ((op1_info & MAY_BE_DOUBLE) &&
8040	           !(op1_info & MAY_BE_LONG) &&
8041	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
8042		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) {
8043			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9
8044		}
8045		if (op2_info & MAY_BE_DOUBLE) {
8046			if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
8047				if (!same_ops && (op2_info & MAY_BE_LONG)) {
8048					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >3
8049				} else {
8050					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
8051				}
8052			}
8053			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8054				return 0;
8055			}
8056		}
8057		if (!same_ops && (op2_info & MAY_BE_LONG)) {
8058			if (op2_info & MAY_BE_DOUBLE) {
8059				|.cold_code
8060			}
8061		    |3:
8062			if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
8063				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
8064			}
8065			if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8066				return 0;
8067			}
8068			if (op2_info & MAY_BE_DOUBLE) {
8069				|	jmp >6
8070				|.code
8071			}
8072		}
8073	} else if ((op2_info & MAY_BE_DOUBLE) &&
8074	           !(op2_info & MAY_BE_LONG) &&
8075	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
8076		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) {
8077			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9
8078		}
8079		if (op1_info & MAY_BE_DOUBLE) {
8080			if (!same_ops && (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
8081				if (!same_ops && (op1_info & MAY_BE_LONG)) {
8082					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >3
8083				} else {
8084					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9
8085				}
8086			}
8087			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8088				return 0;
8089			}
8090		}
8091		if (!same_ops && (op1_info & MAY_BE_LONG)) {
8092			if (op1_info & MAY_BE_DOUBLE) {
8093				|.cold_code
8094			}
8095			|3:
8096			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
8097				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9
8098			}
8099			if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8100				return 0;
8101			}
8102			if (op1_info & MAY_BE_DOUBLE) {
8103				|	jmp >6
8104				|.code
8105			}
8106		}
8107	}
8108
8109	if (has_slow ||
8110	    (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
8111	    (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
8112		if (has_slow) {
8113			|.cold_code
8114			|9:
8115		}
8116		|	SET_EX_OPLINE opline, r0
8117		if (Z_MODE(op1_addr) == IS_REG) {
8118			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
8119			if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
8120				return 0;
8121			}
8122			op1_addr = real_addr;
8123		}
8124		if (Z_MODE(op2_addr) == IS_REG) {
8125			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
8126			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
8127				return 0;
8128			}
8129			op2_addr = real_addr;
8130		}
8131		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8132		if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) {
8133			|	IF_NOT_Z_TYPE FCARG1a, IS_UNDEF, >1
8134			|	mov FCARG1a, opline->op1.var
8135			|	EXT_CALL zend_jit_undefined_op_helper, r0
8136			|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
8137			|1:
8138		}
8139		if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) {
8140			|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1
8141			|	mov T1, FCARG1a // save
8142			|	mov FCARG1a, opline->op2.var
8143			|	EXT_CALL zend_jit_undefined_op_helper, r0
8144			|	mov FCARG1a, T1 // restore
8145			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
8146			|	jmp >2
8147			|1:
8148			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8149			|2:
8150		} else {
8151			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8152		}
8153		|	EXT_CALL zend_compare, r0
8154		if ((opline->opcode != ZEND_CASE &&
8155		     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8156		     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
8157		    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8158		     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
8159			|	mov dword T1, eax // save
8160			if (opline->opcode != ZEND_CASE) {
8161				|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, NULL
8162			}
8163			|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL
8164			if (may_throw) {
8165				zend_jit_check_exception_undef_result(Dst, opline);
8166			}
8167			|	mov eax, dword T1 // restore
8168		} else if (may_throw) {
8169#if ZTS
8170			|	mov dword T1, eax // save
8171#else
8172			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(exception)))) {
8173				|	mov dword T1, eax // save
8174			}
8175#endif
8176			zend_jit_check_exception_undef_result(Dst, opline);
8177#if ZTS
8178			|	mov eax, dword T1 // restore
8179#else
8180			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(exception)))) {
8181				|	mov eax, dword T1 // restore
8182			}
8183#endif
8184		}
8185		if (!zend_jit_cmp_slow(Dst, opline, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8186			return 0;
8187		}
8188		if (has_slow) {
8189			|	jmp >6
8190			|.code
8191		}
8192	}
8193
8194	|6:
8195
8196	return 1;
8197}
8198
8199static int zend_jit_identical(dasm_State    **Dst,
8200                              const zend_op  *opline,
8201                              uint32_t        op1_info,
8202                              zend_ssa_range *op1_range,
8203                              zend_jit_addr   op1_addr,
8204                              uint32_t        op2_info,
8205                              zend_ssa_range *op2_range,
8206                              zend_jit_addr   op2_addr,
8207                              zend_jit_addr   res_addr,
8208                              int             may_throw,
8209                              zend_uchar      smart_branch_opcode,
8210                              uint32_t        target_label,
8211                              uint32_t        target_label2,
8212                              const void     *exit_addr,
8213                              bool       skip_comparison)
8214{
8215	uint32_t identical_label = (uint32_t)-1;
8216	uint32_t not_identical_label = (uint32_t)-1;
8217
8218	if (smart_branch_opcode && !exit_addr) {
8219		if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8220			if (smart_branch_opcode == ZEND_JMPZ) {
8221				not_identical_label = target_label;
8222			} else if (smart_branch_opcode == ZEND_JMPNZ) {
8223				identical_label = target_label;
8224			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
8225				not_identical_label = target_label;
8226				identical_label = target_label2;
8227			} else {
8228				ZEND_UNREACHABLE();
8229			}
8230		} else {
8231			if (smart_branch_opcode == ZEND_JMPZ) {
8232				identical_label = target_label;
8233			} else if (smart_branch_opcode == ZEND_JMPNZ) {
8234				not_identical_label = target_label;
8235			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
8236				identical_label = target_label;
8237				not_identical_label = target_label2;
8238			} else {
8239				ZEND_UNREACHABLE();
8240			}
8241		}
8242	}
8243
8244	if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
8245	    (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
8246		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)) {
8247			return 0;
8248		}
8249		return 1;
8250	} else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE &&
8251	           (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) {
8252		if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
8253			return 0;
8254		}
8255		return 1;
8256	}
8257
8258	if ((op1_info & MAY_BE_UNDEF) && (op2_info & MAY_BE_UNDEF)) {
8259		op1_info |= MAY_BE_NULL;
8260		op2_info |= MAY_BE_NULL;
8261		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8262		|	IF_Z_TYPE FCARG1a, IS_UNDEF, >1
8263		|.cold_code
8264		|1:
8265		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8266		|	SET_EX_OPLINE opline, r0
8267		|	mov FCARG1d, opline->op1.var
8268		|	EXT_CALL zend_jit_undefined_op_helper, r0
8269		if (may_throw) {
8270			zend_jit_check_exception_undef_result(Dst, opline);
8271		}
8272		|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
8273		|	jmp >1
8274		|.code
8275		|1:
8276		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8277		|	IF_Z_TYPE FCARG2a, IS_UNDEF, >1
8278		|.cold_code
8279		|1:
8280		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8281		|	SET_EX_OPLINE opline, r0
8282		|	mov aword T1, FCARG1a // save
8283		|	mov FCARG1d, opline->op2.var
8284		|	EXT_CALL zend_jit_undefined_op_helper, r0
8285		if (may_throw) {
8286			zend_jit_check_exception_undef_result(Dst, opline);
8287		}
8288		|	mov FCARG1a, aword T1 // restore
8289		|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
8290		|	jmp >1
8291		|.code
8292		|1:
8293	} else if (op1_info & MAY_BE_UNDEF) {
8294		op1_info |= MAY_BE_NULL;
8295		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8296		|	IF_Z_TYPE FCARG1a, IS_UNDEF, >1
8297		|.cold_code
8298		|1:
8299		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8300		|	SET_EX_OPLINE opline, r0
8301		|	mov FCARG1d, opline->op1.var
8302		|	EXT_CALL zend_jit_undefined_op_helper, r0
8303		if (may_throw) {
8304			zend_jit_check_exception_undef_result(Dst, opline);
8305		}
8306		|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
8307		|	jmp >1
8308		|.code
8309		|1:
8310		if (opline->op2_type != IS_CONST) {
8311			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8312		}
8313	} else if (op2_info & MAY_BE_UNDEF) {
8314		op2_info |= MAY_BE_NULL;
8315		|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8316		|	IF_Z_TYPE FCARG2a, IS_UNDEF, >1
8317		|.cold_code
8318		|1:
8319		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
8320		|	SET_EX_OPLINE opline, r0
8321		|	mov FCARG1d, opline->op2.var
8322		|	EXT_CALL zend_jit_undefined_op_helper, r0
8323		if (may_throw) {
8324			zend_jit_check_exception_undef_result(Dst, opline);
8325		}
8326		|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
8327		|	jmp >1
8328		|.code
8329		|1:
8330		if (opline->op1_type != IS_CONST) {
8331			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8332		}
8333	} else if ((op1_info & op2_info & MAY_BE_ANY) != 0) {
8334		if (opline->op1_type != IS_CONST) {
8335			if (Z_MODE(op1_addr) == IS_REG) {
8336				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
8337				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
8338					return 0;
8339				}
8340				op1_addr = real_addr;
8341			}
8342		}
8343		if (opline->op2_type != IS_CONST) {
8344			if (Z_MODE(op2_addr) == IS_REG) {
8345				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
8346				if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
8347					return 0;
8348				}
8349				op2_addr = real_addr;
8350			}
8351			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8352		}
8353		if (opline->op1_type != IS_CONST) {
8354			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8355		}
8356	}
8357
8358	if ((op1_info & op2_info & MAY_BE_ANY) == 0) {
8359		if ((opline->opcode != ZEND_CASE_STRICT &&
8360		     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8361		     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
8362		    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8363		     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
8364			if (opline->opcode != ZEND_CASE_STRICT) {
8365				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8366			}
8367			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8368		}
8369		if (smart_branch_opcode) {
8370			if (may_throw) {
8371				zend_jit_check_exception_undef_result(Dst, opline);
8372			}
8373			if (exit_addr) {
8374				if (smart_branch_opcode == ZEND_JMPZ) {
8375					|	jmp &exit_addr
8376				}
8377			} else if (not_identical_label != (uint32_t)-1) {
8378				|	jmp =>not_identical_label
8379			}
8380		} else {
8381			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE)
8382			if (may_throw) {
8383				zend_jit_check_exception(Dst);
8384			}
8385		}
8386		return 1;
8387	}
8388
8389	if (opline->op1_type & (IS_CV|IS_VAR)) {
8390		|	ZVAL_DEREF FCARG1a, op1_info
8391	}
8392	if (opline->op2_type & (IS_CV|IS_VAR)) {
8393		|	ZVAL_DEREF FCARG2a, op2_info
8394	}
8395
8396	if (has_concrete_type(op1_info)
8397	 && has_concrete_type(op2_info)
8398	 && concrete_type(op1_info) == concrete_type(op2_info)
8399	 && concrete_type(op1_info) <= IS_TRUE) {
8400		if (smart_branch_opcode) {
8401			if (exit_addr) {
8402				if (smart_branch_opcode == ZEND_JMPNZ) {
8403					|	jmp &exit_addr
8404				}
8405			} else if (identical_label != (uint32_t)-1) {
8406				|	jmp =>identical_label
8407			}
8408		} else {
8409			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE)
8410		}
8411	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) == IS_CONST_ZVAL) {
8412		if (zend_is_identical(Z_ZV(op1_addr), Z_ZV(op2_addr))) {
8413			if (smart_branch_opcode) {
8414				if (exit_addr) {
8415					if (smart_branch_opcode == ZEND_JMPNZ) {
8416						|	jmp &exit_addr
8417					}
8418				} else if (identical_label != (uint32_t)-1) {
8419					|	jmp =>identical_label
8420				}
8421			} else {
8422				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE)
8423			}
8424		} else {
8425			if (smart_branch_opcode) {
8426				if (exit_addr) {
8427					if (smart_branch_opcode == ZEND_JMPZ) {
8428						|	jmp &exit_addr
8429					}
8430				} else if (not_identical_label != (uint32_t)-1) {
8431					|	jmp =>not_identical_label
8432				}
8433			} else {
8434				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE)
8435			}
8436		}
8437	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op1_addr)) <= IS_TRUE) {
8438		zval *val = Z_ZV(op1_addr);
8439
8440		|	cmp byte [FCARG2a + offsetof(zval, u1.v.type)], Z_TYPE_P(val)
8441		if (smart_branch_opcode) {
8442			if (opline->op2_type == IS_VAR && (op2_info & MAY_BE_REF)) {
8443				|	jne >8
8444				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8445				if (may_throw) {
8446					zend_jit_check_exception_undef_result(Dst, opline);
8447				}
8448				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8449					|	jmp &exit_addr
8450				} else if (identical_label != (uint32_t)-1) {
8451					|	jmp =>identical_label
8452				} else {
8453					|	jmp >9
8454				}
8455				|8:
8456			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8457				|	je &exit_addr
8458			} else if (identical_label != (uint32_t)-1) {
8459				|	je =>identical_label
8460			} else {
8461				|	je >9
8462			}
8463		} else {
8464			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8465				|	sete al
8466			} else {
8467				|	setne al
8468			}
8469			|	movzx eax, al
8470			|	lea eax, [eax + 2]
8471			|	SET_ZVAL_TYPE_INFO res_addr, eax
8472		}
8473		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8474		    (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
8475			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8476			if (may_throw) {
8477				zend_jit_check_exception_undef_result(Dst, opline);
8478			}
8479		}
8480		if (exit_addr) {
8481			if (smart_branch_opcode == ZEND_JMPZ) {
8482				|	jmp &exit_addr
8483			}
8484		} else if (smart_branch_opcode && not_identical_label != (uint32_t)-1) {
8485			|	jmp =>not_identical_label
8486		}
8487	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op2_addr)) <= IS_TRUE) {
8488		zval *val = Z_ZV(op2_addr);
8489
8490		|	cmp byte [FCARG1a + offsetof(zval, u1.v.type)], Z_TYPE_P(val)
8491		if (smart_branch_opcode) {
8492			if (opline->opcode != ZEND_CASE_STRICT
8493			 && opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) {
8494				|	jne >8
8495				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8496				if (may_throw) {
8497					zend_jit_check_exception_undef_result(Dst, opline);
8498				}
8499				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8500					|	jmp &exit_addr
8501				} else if (identical_label != (uint32_t)-1) {
8502					|	jmp =>identical_label
8503				} else {
8504					|	jmp >9
8505				}
8506				|8:
8507			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
8508				|	je &exit_addr
8509			} else if (identical_label != (uint32_t)-1) {
8510				|	je =>identical_label
8511			} else {
8512				|	je >9
8513			}
8514		} else {
8515			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8516				|	sete al
8517			} else {
8518				|	setne al
8519			}
8520			|	movzx eax, al
8521			|	lea eax, [eax + 2]
8522			|	SET_ZVAL_TYPE_INFO res_addr, eax
8523		}
8524		if (opline->opcode != ZEND_CASE_STRICT
8525		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8526		    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
8527			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8528			if (may_throw) {
8529				zend_jit_check_exception_undef_result(Dst, opline);
8530			}
8531		}
8532		if (smart_branch_opcode) {
8533			if (exit_addr) {
8534				if (smart_branch_opcode == ZEND_JMPZ) {
8535					|	jmp &exit_addr
8536				}
8537			} else if (not_identical_label != (uint32_t)-1) {
8538				|	jmp =>not_identical_label
8539			}
8540		}
8541	} else {
8542		if (opline->op1_type == IS_CONST) {
8543			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8544		}
8545		if (opline->op2_type == IS_CONST) {
8546			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
8547		}
8548		|	EXT_CALL zend_is_identical, r0
8549			if ((opline->opcode != ZEND_CASE_STRICT &&
8550			     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8551			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
8552			    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
8553			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
8554				|	mov aword T1, r0 // save
8555				if (opline->opcode != ZEND_CASE_STRICT) {
8556					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
8557				}
8558				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline
8559				if (may_throw) {
8560					zend_jit_check_exception_undef_result(Dst, opline);
8561				}
8562				|	mov r0, aword T1 // restore
8563			}
8564		if (smart_branch_opcode) {
8565			|	test al, al
8566			if (exit_addr) {
8567				if (smart_branch_opcode == ZEND_JMPNZ) {
8568					|	jnz &exit_addr
8569				} else {
8570					|	jz &exit_addr
8571				}
8572			} else if (not_identical_label != (uint32_t)-1) {
8573				|	jz =>not_identical_label
8574				if (identical_label != (uint32_t)-1) {
8575					|	jmp =>identical_label
8576				}
8577			} else if (identical_label != (uint32_t)-1) {
8578				|	jnz =>identical_label
8579			}
8580		} else {
8581			|	movzx eax, al
8582			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
8583				|	lea eax, [eax + 2]
8584			} else {
8585				|	neg eax
8586				|	lea eax, [eax + 3]
8587			}
8588			|	SET_ZVAL_TYPE_INFO res_addr, eax
8589		}
8590	}
8591
8592	|9:
8593	if (may_throw) {
8594		zend_jit_check_exception(Dst);
8595	}
8596	return 1;
8597}
8598
8599static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr res_addr, uint32_t target_label, uint32_t target_label2, int may_throw, zend_uchar branch_opcode, const void *exit_addr)
8600{
8601	uint32_t true_label = -1;
8602	uint32_t false_label = -1;
8603	bool set_bool = 0;
8604	bool set_bool_not = 0;
8605	bool set_delayed = 0;
8606	bool jmp_done = 0;
8607
8608	if (branch_opcode == ZEND_BOOL) {
8609		set_bool = 1;
8610	} else if (branch_opcode == ZEND_BOOL_NOT) {
8611		set_bool = 1;
8612		set_bool_not = 1;
8613	} else if (branch_opcode == ZEND_JMPZ) {
8614		false_label = target_label;
8615	} else if (branch_opcode == ZEND_JMPNZ) {
8616		true_label = target_label;
8617	} else if (branch_opcode == ZEND_JMPZNZ) {
8618		true_label = target_label2;
8619		false_label = target_label;
8620	} else if (branch_opcode == ZEND_JMPZ_EX) {
8621		set_bool = 1;
8622		false_label = target_label;
8623	} else if (branch_opcode == ZEND_JMPNZ_EX) {
8624		set_bool = 1;
8625		true_label = target_label;
8626	} else {
8627		ZEND_UNREACHABLE();
8628	}
8629
8630	if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
8631		if (zend_is_true(Z_ZV(op1_addr))) {
8632			/* Always TRUE */
8633			if (set_bool) {
8634				if (set_bool_not) {
8635					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8636				} else {
8637					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8638				}
8639			}
8640			if (true_label != (uint32_t)-1) {
8641				|	jmp =>true_label;
8642			}
8643		} else {
8644			/* Always FALSE */
8645			if (set_bool) {
8646				if (set_bool_not) {
8647					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8648				} else {
8649					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8650				}
8651			}
8652			if (false_label != (uint32_t)-1) {
8653				|	jmp =>false_label;
8654			}
8655		}
8656		return 1;
8657	}
8658
8659	if (opline->op1_type == IS_CV && (op1_info & MAY_BE_REF)) {
8660		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8661		|	ZVAL_DEREF FCARG1a, op1_info
8662		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
8663	}
8664
8665	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE)) {
8666		if (!(op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_TRUE))) {
8667			/* Always TRUE */
8668			if (set_bool) {
8669				if (set_bool_not) {
8670					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8671				} else {
8672					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8673				}
8674			}
8675			if (true_label != (uint32_t)-1) {
8676				|	jmp =>true_label;
8677			}
8678		} else {
8679			if (!(op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE)))) {
8680				/* Always FALSE */
8681				if (set_bool) {
8682					if (set_bool_not) {
8683						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8684					} else {
8685						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8686					}
8687				}
8688			} else {
8689				|	CMP_ZVAL_TYPE op1_addr, IS_TRUE
8690				if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
8691				    if ((op1_info & MAY_BE_LONG) &&
8692				        !(op1_info & MAY_BE_UNDEF) &&
8693				        !set_bool) {
8694						if (exit_addr) {
8695							if (branch_opcode == ZEND_JMPNZ) {
8696								|	jl >9
8697							} else {
8698								|	jl &exit_addr
8699							}
8700						} else if (false_label != (uint32_t)-1) {
8701							|	jl =>false_label
8702						} else {
8703							|	jl >9
8704						}
8705						jmp_done = 1;
8706					} else {
8707						|	jg >2
8708					}
8709				}
8710				if (!(op1_info & MAY_BE_TRUE)) {
8711					/* It's FALSE */
8712					if (set_bool) {
8713						if (set_bool_not) {
8714							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8715						} else {
8716							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8717						}
8718					}
8719				} else {
8720					if (exit_addr) {
8721						if (set_bool) {
8722							|	jne >1
8723							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8724							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8725								|	jmp &exit_addr
8726							} else {
8727								|	jmp >9
8728							}
8729							|1:
8730							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8731							if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
8732								if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8733									|	jne &exit_addr
8734								}
8735							}
8736						} else {
8737							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8738								|	je &exit_addr
8739							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8740								|	jne &exit_addr
8741							} else {
8742								|	je >9
8743							}
8744						}
8745					} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8746						if (set_bool) {
8747							|	jne >1
8748							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8749							if (true_label != (uint32_t)-1) {
8750								|	jmp =>true_label
8751							} else {
8752								|	jmp >9
8753							}
8754							|1:
8755							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8756						} else {
8757							if (true_label != (uint32_t)-1) {
8758								|	je =>true_label
8759							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8760								|	jne =>false_label
8761								jmp_done = 1;
8762							} else {
8763								|	je >9
8764							}
8765						}
8766					} else if (set_bool) {
8767						|	sete al
8768						|	movzx eax, al
8769						if (set_bool_not) {
8770							|	neg eax
8771							|	add eax, 3
8772						} else {
8773							|	add eax, 2
8774						}
8775						if ((op1_info & MAY_BE_UNDEF) && (op1_info & MAY_BE_ANY)) {
8776							set_delayed = 1;
8777						} else {
8778							|	SET_ZVAL_TYPE_INFO res_addr, eax
8779						}
8780					}
8781				}
8782			}
8783
8784			/* It's FALSE, but may be UNDEF */
8785			if (op1_info & MAY_BE_UNDEF) {
8786				if (op1_info & MAY_BE_ANY) {
8787					if (set_delayed) {
8788						|	CMP_ZVAL_TYPE op1_addr, IS_UNDEF
8789						|	SET_ZVAL_TYPE_INFO res_addr, eax
8790						|	jz >1
8791					} else {
8792						|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1
8793					}
8794					|.cold_code
8795					|1:
8796				}
8797				|	mov FCARG1d, opline->op1.var
8798				|	SET_EX_OPLINE opline, r0
8799				|	EXT_CALL zend_jit_undefined_op_helper, r0
8800
8801				if (may_throw) {
8802					if (!zend_jit_check_exception_undef_result(Dst, opline)) {
8803						return 0;
8804					}
8805				}
8806
8807				if (exit_addr) {
8808					if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
8809						|	jmp &exit_addr
8810					}
8811				} else if (false_label != (uint32_t)-1) {
8812					|	jmp =>false_label
8813				}
8814				if (op1_info & MAY_BE_ANY) {
8815					if (exit_addr) {
8816						if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8817							|	jmp >9
8818						}
8819					} else if (false_label == (uint32_t)-1) {
8820						|	jmp >9
8821					}
8822					|.code
8823				}
8824			}
8825
8826			if (!jmp_done) {
8827				if (exit_addr) {
8828					if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8829						if (op1_info & MAY_BE_LONG) {
8830							|	jmp >9
8831						}
8832					} else if (op1_info & MAY_BE_LONG) {
8833						|	jmp &exit_addr
8834					}
8835				} else if (false_label != (uint32_t)-1) {
8836					|	jmp =>false_label
8837				} else if ((op1_info & MAY_BE_LONG) || (op1_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
8838					|	jmp >9
8839				}
8840			}
8841		}
8842	}
8843
8844	if (op1_info & MAY_BE_LONG) {
8845		|2:
8846		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
8847			|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2
8848		}
8849		if (Z_MODE(op1_addr) == IS_REG) {
8850			|	test Ra(Z_REG(op1_addr)), Ra(Z_REG(op1_addr))
8851		} else {
8852			|	LONG_OP_WITH_CONST, cmp, op1_addr, Z_L(0)
8853		}
8854		if (set_bool) {
8855			|	setne al
8856			|	movzx eax, al
8857			if (set_bool_not) {
8858				|	neg eax
8859				|	add eax, 3
8860			} else {
8861				|	lea eax, [eax + 2]
8862			}
8863			|	SET_ZVAL_TYPE_INFO res_addr, eax
8864		}
8865		if (exit_addr) {
8866			if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8867				|	jne &exit_addr
8868			} else {
8869				|	je &exit_addr
8870			}
8871		} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8872			if (true_label != (uint32_t)-1) {
8873				|	jne =>true_label
8874				if (false_label != (uint32_t)-1) {
8875					|	jmp =>false_label
8876				}
8877			} else {
8878				|	je =>false_label
8879			}
8880		}
8881	}
8882
8883	if ((op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) == MAY_BE_DOUBLE) {
8884		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8885			|.cold_code
8886		}
8887		|2:
8888		if (CAN_USE_AVX()) {
8889			|	vxorps xmm0, xmm0, xmm0
8890		} else {
8891			|	xorps xmm0, xmm0
8892		}
8893		|	DOUBLE_CMP ZREG_XMM0, op1_addr
8894
8895		if (set_bool) {
8896			if (exit_addr) {
8897				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8898					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8899					|	jp &exit_addr
8900					|	jne &exit_addr
8901					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8902				} else {
8903					|	jp >1
8904					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8905					|	je &exit_addr
8906					|1:
8907					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8908				}
8909			} else if (false_label != (uint32_t)-1) { // JMPZ_EX (p=>true, z=>false, false=>jmp)
8910				|	jp  >1
8911				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8912				|	je  => false_label
8913				|1:
8914				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8915			} else if (true_label != (uint32_t)-1) { // JMPNZ_EX (p=>true, z=>false, true=>jmp)
8916				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
8917				|	jp  => true_label
8918				|	jne  => true_label
8919				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
8920			} else if (set_bool_not) { // BOOL_NOT (p=>false, z=>true)
8921				|	mov eax, IS_FALSE
8922				|	jp >1
8923				|	jne >1
8924				|	mov eax, IS_TRUE
8925				|1:
8926				|	SET_ZVAL_TYPE_INFO res_addr, eax
8927			} else { // BOOL (p=>true, z=>false)
8928				|	mov eax, IS_TRUE
8929				|	jp >1
8930				|	jne >1
8931				|	mov eax, IS_FALSE
8932				|1:
8933				|	SET_ZVAL_TYPE_INFO res_addr, eax
8934			}
8935			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8936				|	jmp >9
8937				|.code
8938			}
8939		} else {
8940			if (exit_addr) {
8941				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8942					|	jp &exit_addr
8943					|	jne &exit_addr
8944					|1:
8945				} else {
8946					|	jp >1
8947					|	je &exit_addr
8948					|1:
8949				}
8950				if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8951					|	jmp >9
8952				}
8953			} else {
8954				ZEND_ASSERT(true_label != (uint32_t)-1 || false_label != (uint32_t)-1);
8955				if (false_label != (uint32_t)-1 ) {
8956					|	jp  >1
8957					|	je  => false_label
8958					|1:
8959					if (true_label != (uint32_t)-1) {
8960						|	jmp =>true_label
8961					} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8962						|	jmp >9
8963					}
8964				} else {
8965					|	jp  => true_label
8966					|	jne  => true_label
8967					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8968						|	jmp >9
8969					}
8970				}
8971			}
8972			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8973				|.code
8974			}
8975		}
8976	} else if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
8977		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8978			|.cold_code
8979			|2:
8980		}
8981		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
8982			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
8983		}
8984		|	SET_EX_OPLINE opline, r0
8985		|	EXT_CALL zend_is_true, r0
8986
8987		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8988			(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
8989			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
8990
8991			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
8992				|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >3
8993			}
8994			|	GET_ZVAL_PTR FCARG1a, op1_addr
8995			|	GC_DELREF FCARG1a
8996			|	jnz >3
8997			|	mov aword T1, r0 // save
8998			|	ZVAL_DTOR_FUNC op1_info, opline
8999			|	mov r0, aword T1 // restore
9000			|3:
9001		}
9002		if (may_throw) {
9003			|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r1
9004			|	jne ->exception_handler_undef
9005		}
9006
9007		if (set_bool) {
9008			if (set_bool_not) {
9009				|	neg eax
9010				|	add eax, 3
9011			} else {
9012				|	add eax, 2
9013			}
9014			|	SET_ZVAL_TYPE_INFO res_addr, eax
9015			if (exit_addr) {
9016				|	CMP_ZVAL_TYPE res_addr, IS_FALSE
9017				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
9018					|	jne &exit_addr
9019				} else {
9020					|	je &exit_addr
9021				}
9022			} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
9023				|	CMP_ZVAL_TYPE res_addr, IS_FALSE
9024				if (true_label != (uint32_t)-1) {
9025					|	jne =>true_label
9026					if (false_label != (uint32_t)-1) {
9027						|	jmp =>false_label
9028					} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
9029						|	jmp >9
9030					}
9031				} else {
9032					|	je =>false_label
9033				}
9034			}
9035			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
9036				|	jmp >9
9037				|.code
9038			}
9039		} else {
9040			|	test r0, r0
9041			if (exit_addr) {
9042				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
9043					|	jne &exit_addr
9044					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
9045						|	jmp >9
9046					}
9047				} else {
9048					|	je &exit_addr
9049					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
9050						|	jmp >9
9051					}
9052				}
9053			} else if (true_label != (uint32_t)-1) {
9054				|	jne =>true_label
9055				if (false_label != (uint32_t)-1) {
9056					|	jmp =>false_label
9057				} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
9058					|	jmp >9
9059				}
9060			} else {
9061				|	je =>false_label
9062				if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
9063					|	jmp >9
9064				}
9065			}
9066
9067			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
9068				|.code
9069			}
9070		}
9071	}
9072
9073	|9:
9074
9075	return 1;
9076}
9077
9078static 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)
9079{
9080	if (op1_addr != op1_def_addr) {
9081		if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
9082			return 0;
9083		}
9084		if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
9085			op1_addr = op1_def_addr;
9086		}
9087	}
9088
9089	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)) {
9090		return 0;
9091	}
9092	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
9093		return 0;
9094	}
9095	if (op1_info & MAY_BE_UNDEF) {
9096		zend_jit_check_exception(Dst);
9097	}
9098	return 1;
9099}
9100
9101static 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)
9102{
9103	ZEND_ASSERT(opline->op1_type == IS_CV);
9104
9105	if (op2_addr != op2_def_addr) {
9106		if (!zend_jit_update_regs(Dst, opline->op2.var, op2_addr, op2_def_addr, op2_info)) {
9107			return 0;
9108		}
9109		if (Z_MODE(op2_def_addr) == IS_REG && Z_MODE(op2_addr) != IS_REG) {
9110			op2_addr = op2_def_addr;
9111		}
9112	}
9113
9114	if (Z_MODE(op1_addr) != IS_REG
9115	 && Z_MODE(op1_use_addr) == IS_REG
9116	 && !Z_LOAD(op1_use_addr)
9117	 && !Z_STORE(op1_use_addr)) {
9118		/* Force type update */
9119		op1_info |= MAY_BE_UNDEF;
9120	}
9121	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,
9122			may_throw)) {
9123		return 0;
9124	}
9125	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_addr, op1_def_info, op1_use_addr, op1_info)) {
9126		return 0;
9127	}
9128	if (opline->result_type != IS_UNUSED) {
9129		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
9130			return 0;
9131		}
9132	}
9133
9134	return 1;
9135}
9136
9137/* copy of hidden zend_closure */
9138typedef struct _zend_closure {
9139	zend_object       std;
9140	zend_function     func;
9141	zval              this_ptr;
9142	zend_class_entry *called_scope;
9143	zif_handler       orig_internal_handler;
9144} zend_closure;
9145
9146static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_t used_stack)
9147{
9148	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9149	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9150
9151	if (!exit_addr) {
9152		return 0;
9153	}
9154
9155	|	// Check Stack Overflow
9156	|	MEM_LOAD_ZTS r1, aword, executor_globals, vm_stack_end, r0
9157	|	MEM_LOAD_OP_ZTS sub, r1, aword, executor_globals, vm_stack_top, r0
9158	|	cmp r1, used_stack
9159	|	jb &exit_addr
9160
9161	return 1;
9162}
9163
9164static 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)
9165{
9166	uint32_t used_stack;
9167	bool stack_check = 1;
9168
9169	if (func) {
9170		used_stack = zend_vm_calc_used_stack(opline->extended_value, func);
9171		if ((int)used_stack <= checked_stack) {
9172			stack_check = 0;
9173		}
9174	} else {
9175		used_stack = (ZEND_CALL_FRAME_SLOT + opline->extended_value) * sizeof(zval);
9176
9177		|	// if (EXPECTED(ZEND_USER_CODE(func->type))) {
9178		if (!is_closure) {
9179			|	test byte [r0 + offsetof(zend_function, type)], 1
9180			|	mov FCARG1a, used_stack
9181			|	jnz >1
9182		} else {
9183			|	mov FCARG1a, used_stack
9184		}
9185		|	// used_stack += (func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args)) * sizeof(zval);
9186		|	mov edx, opline->extended_value
9187		if (!is_closure) {
9188			|	cmp edx, dword [r0 + offsetof(zend_function, op_array.num_args)]
9189			|	cmova edx, dword [r0 + offsetof(zend_function, op_array.num_args)]
9190			|	sub edx, dword [r0 + offsetof(zend_function, op_array.last_var)]
9191			|	sub edx, dword [r0 + offsetof(zend_function, op_array.T)]
9192		} else {
9193			|	cmp edx, dword [r0 + offsetof(zend_closure, func.op_array.num_args)]
9194			|	cmova edx, dword [r0 + offsetof(zend_closure, func.op_array.num_args)]
9195			|	sub edx, dword [r0 + offsetof(zend_closure, func.op_array.last_var)]
9196			|	sub edx, dword [r0 + offsetof(zend_closure, func.op_array.T)]
9197		}
9198		|	shl edx, 4
9199		|.if X64
9200			|	movsxd r2, edx
9201		|.endif
9202		|	sub FCARG1a, r2
9203		|1:
9204	}
9205
9206	zend_jit_start_reuse_ip();
9207
9208	|	// if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
9209	|	MEM_LOAD_ZTS RX, aword, executor_globals, vm_stack_top, RX
9210
9211	if (stack_check) {
9212		|	// Check Stack Overflow
9213		|	MEM_LOAD_ZTS r2, aword, executor_globals, vm_stack_end, r2
9214		|	sub r2, RX
9215		if (func) {
9216			|	cmp r2, used_stack
9217		} else {
9218			|	cmp r2, FCARG1a
9219		}
9220
9221		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9222			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9223			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9224
9225			if (!exit_addr) {
9226				return 0;
9227			}
9228
9229			|	jb &exit_addr
9230		} else {
9231			|	jb >1
9232			|	// EG(vm_stack_top) = (zval*)((char*)call + used_stack);
9233			|.cold_code
9234			|1:
9235			if (func) {
9236				|	mov FCARG1d, used_stack
9237			}
9238#ifdef _WIN32
9239			if (0) {
9240#else
9241			if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
9242#endif
9243				|	SET_EX_OPLINE opline, r0
9244				|	EXT_CALL zend_jit_int_extend_stack_helper, r0
9245			} else {
9246				if (!is_closure) {
9247					if (func
9248					 && op_array == &func->op_array
9249					 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
9250					 && (sizeof(void*) != 8 || IS_SIGNED_32BIT(func))) {
9251						|	LOAD_ADDR FCARG2a, func
9252					} else {
9253						|	mov FCARG2a, r0
9254					}
9255				} else {
9256					|	lea FCARG2a, aword [r0 + offsetof(zend_closure, func)]
9257				}
9258				|	SET_EX_OPLINE opline, r0
9259				|	EXT_CALL zend_jit_extend_stack_helper, r0
9260			}
9261			|	mov RX, r0
9262			|	jmp >1
9263			|.code
9264		}
9265	}
9266
9267	if (func) {
9268		|	MEM_UPDATE_ZTS add, aword, executor_globals, vm_stack_top, used_stack, r2
9269	} else {
9270		|	MEM_UPDATE_ZTS add, aword, executor_globals, vm_stack_top, FCARG1a, r2
9271	}
9272	|	// zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
9273	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) {
9274		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
9275		|	mov dword EX:RX->This.u1.type_info, (IS_UNDEF | ZEND_CALL_NESTED_FUNCTION)
9276	}
9277#ifdef _WIN32
9278	if (0) {
9279#else
9280	if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
9281#endif
9282		|	// call->func = func;
9283		|1:
9284		|	ADDR_STORE aword EX:RX->func, func, r1
9285	} else {
9286		if (!is_closure) {
9287			|	// call->func = func;
9288			if (func
9289			 && op_array == &func->op_array
9290			 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
9291			 && (sizeof(void*) != 8 || IS_SIGNED_32BIT(func))) {
9292				|	ADDR_STORE aword EX:RX->func, func, r1
9293			} else {
9294				|	mov aword EX:RX->func, r0
9295			}
9296		} else {
9297			|	// call->func = &closure->func;
9298			|	lea r1, aword [r0 + offsetof(zend_closure, func)]
9299			|	mov aword EX:RX->func, r1
9300		}
9301		|1:
9302	}
9303	if (opline->opcode == ZEND_INIT_METHOD_CALL) {
9304		|	// Z_PTR(call->This) = obj;
9305		|	mov r1, aword T1
9306		|	mov aword EX:RX->This.value.ptr, r1
9307	    if (opline->op1_type == IS_UNUSED || delayed_fetch_this) {
9308			|	// call->call_info |= ZEND_CALL_HAS_THIS;
9309			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9310				|	mov dword EX:RX->This.u1.type_info, ZEND_CALL_HAS_THIS
9311			} else {
9312				|	or dword EX:RX->This.u1.type_info, ZEND_CALL_HAS_THIS
9313			}
9314	    } else {
9315			if (opline->op1_type == IS_CV) {
9316				|	// GC_ADDREF(obj);
9317				|	add dword [r1], 1
9318			}
9319			|	// call->call_info |= ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS;
9320			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9321				|	mov dword EX:RX->This.u1.type_info, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS)
9322			} else {
9323				|	or dword EX:RX->This.u1.type_info, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS)
9324			}
9325	    }
9326	} else if (!is_closure) {
9327		|	// Z_CE(call->This) = called_scope;
9328		|	mov aword EX:RX->This.value.ptr, 0
9329	} else {
9330		if (opline->op2_type == IS_CV) {
9331			|	// GC_ADDREF(closure);
9332			|	add dword [r0], 1
9333		}
9334		|	//	object_or_called_scope = closure->called_scope;
9335		|	mov r1, aword [r0 + offsetof(zend_closure, called_scope)]
9336		|	mov aword EX:RX->This.value.ptr, r1
9337		|	// call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE |
9338		|	//	(closure->func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE);
9339		|	mov edx, dword [r0 + offsetof(zend_closure, func.common.fn_flags)]
9340		|	and edx, ZEND_ACC_FAKE_CLOSURE
9341		|	or edx, (ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE)
9342		|	//	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
9343		|	cmp byte [r0 + offsetof(zend_closure, this_ptr.u1.v.type)], IS_UNDEF
9344		|	jz >1
9345		|	//	call_info |= ZEND_CALL_HAS_THIS;
9346		|	or edx, ZEND_CALL_HAS_THIS
9347		|	//	object_or_called_scope = Z_OBJ(closure->this_ptr);
9348		|	mov r1, aword [r0 + offsetof(zend_closure, this_ptr.value.ptr)]
9349	    |1:
9350		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
9351		|	or dword EX:RX->This.u1.type_info, edx
9352		|	// Z_PTR(call->This) = object_or_called_scope;
9353		|	mov aword EX:RX->This.value.ptr, r1
9354		|	cmp aword [r0 + offsetof(zend_closure, func.op_array.run_time_cache__ptr)], 0
9355		|	jnz >1
9356		|	lea FCARG1a, aword [r0 + offsetof(zend_closure, func)]
9357		|	EXT_CALL zend_jit_init_func_run_time_cache_helper, r0
9358		|1:
9359	}
9360	|	// ZEND_CALL_NUM_ARGS(call) = num_args;
9361	|	mov dword EX:RX->This.u2.num_args, opline->extended_value
9362	return 1;
9363}
9364
9365static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zend_function *func, const zend_op *to_opline)
9366{
9367	int32_t exit_point;
9368	const void *exit_addr;
9369
9370	if (func->type == ZEND_INTERNAL_FUNCTION) {
9371#ifdef ZEND_WIN32
9372		// TODO: ASLR may cause different addresses in different workers ???
9373		return 0;
9374#endif
9375	} else if (func->type == ZEND_USER_FUNCTION) {
9376		if (!zend_accel_in_shm(func->op_array.opcodes)) {
9377			/* op_array and op_array->opcodes are not persistent. We can't link. */
9378			return 0;
9379		}
9380	} else {
9381		ZEND_UNREACHABLE();
9382		return 0;
9383	}
9384
9385	exit_point = zend_jit_trace_get_exit_point(to_opline, ZEND_JIT_EXIT_POLYMORPHISM);
9386	exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9387	if (!exit_addr) {
9388		return 0;
9389	}
9390
9391	|	// call = EX(call);
9392	|	mov r1, EX->call
9393	while (level > 0) {
9394		|	mov r1, EX:r1->prev_execute_data
9395		level--;
9396	}
9397
9398	if (func->type == ZEND_USER_FUNCTION &&
9399	    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
9400	     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
9401	     !func->common.function_name)) {
9402		const zend_op *opcodes = func->op_array.opcodes;
9403
9404		|	mov r1, aword EX:r1->func
9405		|   .if X64
9406		||		if (!IS_SIGNED_32BIT(opcodes)) {
9407		|			mov64 r2, ((ptrdiff_t)opcodes)
9408		|			cmp aword [r1 + offsetof(zend_op_array, opcodes)], r2
9409		||		} else {
9410		|			cmp aword [r1 + offsetof(zend_op_array, opcodes)], opcodes
9411		||		}
9412		|	.else
9413		|		cmp aword [r1 + offsetof(zend_op_array, opcodes)], opcodes
9414		|	.endif
9415		|	jne &exit_addr
9416	} else {
9417		|   .if X64
9418		||		if (!IS_SIGNED_32BIT(func)) {
9419		|			mov64 r2, ((ptrdiff_t)func)
9420		|			cmp aword EX:r1->func, r2
9421		||		} else {
9422		|			cmp aword EX:r1->func, func
9423		||		}
9424		|	.else
9425		|		cmp aword EX:r1->func, func
9426		|	.endif
9427		|	jne &exit_addr
9428	}
9429
9430	return 1;
9431}
9432
9433static 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)
9434{
9435	zend_func_info *info = ZEND_FUNC_INFO(op_array);
9436	zend_call_info *call_info = NULL;
9437	zend_function *func = NULL;
9438
9439	if (delayed_call_chain) {
9440		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9441			return 0;
9442		}
9443	}
9444
9445	if (info) {
9446		call_info = info->callee_info;
9447		while (call_info && call_info->caller_init_opline != opline) {
9448			call_info = call_info->next_callee;
9449		}
9450		if (call_info && call_info->callee_func && !call_info->is_prototype) {
9451			func = call_info->callee_func;
9452		}
9453	}
9454
9455	if (!func
9456	 && trace
9457	 && trace->op == ZEND_JIT_TRACE_INIT_CALL) {
9458#ifdef _WIN32
9459		/* ASLR */
9460		if (trace->func->type != ZEND_INTERNAL_FUNCTION) {
9461			func = (zend_function*)trace->func;
9462		}
9463#else
9464		func = (zend_function*)trace->func;
9465#endif
9466	}
9467
9468#ifdef _WIN32
9469	if (0) {
9470#else
9471	if (opline->opcode == ZEND_INIT_FCALL
9472	 && func
9473	 && func->type == ZEND_INTERNAL_FUNCTION) {
9474#endif
9475		/* load constant address later */
9476	} else if (func && op_array == &func->op_array) {
9477		/* recursive call */
9478		if (!(func->op_array.fn_flags & ZEND_ACC_IMMUTABLE) ||
9479		    (sizeof(void*) == 8 && !IS_SIGNED_32BIT(func))) {
9480			|	mov r0, EX->func
9481		}
9482	} else {
9483		|	// if (CACHED_PTR(opline->result.num))
9484		|	mov r2, EX->run_time_cache
9485		|	mov r0, aword [r2 + opline->result.num]
9486		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
9487		 && func
9488		 && (func->common.fn_flags & ZEND_ACC_IMMUTABLE)
9489		 && opline->opcode != ZEND_INIT_FCALL) {
9490			/* Called func may be changed because of recompilation. See ext/opcache/tests/jit/init_fcall_003.phpt */
9491			|   .if X64
9492			||		if (!IS_SIGNED_32BIT(func)) {
9493			|			mov64 r1, ((ptrdiff_t)func)
9494			|			cmp r0, r1
9495			||		} else {
9496			|			cmp r0, func
9497			||		}
9498			|	.else
9499			|		cmp r0, func
9500			|	.endif
9501			|	jnz >1
9502			|.cold_code
9503			|1:
9504		} else {
9505			|	test r0, r0
9506			|	jz >1
9507		}
9508		|.cold_code
9509		|1:
9510		if (opline->opcode == ZEND_INIT_FCALL
9511		 && func
9512		 && func->type == ZEND_USER_FUNCTION
9513		 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) {
9514			|	LOAD_ADDR FCARG1a, func
9515			|	mov aword [r2 + opline->result.num], FCARG1a
9516			|	EXT_CALL zend_jit_init_func_run_time_cache_helper, r0
9517			|	jmp >3
9518		} else {
9519			zval *zv = RT_CONSTANT(opline, opline->op2);
9520
9521			if (opline->opcode == ZEND_INIT_FCALL) {
9522				|	LOAD_ADDR FCARG1a, Z_STR_P(zv);
9523				|	lea FCARG2a, aword [r2 + opline->result.num]
9524				|	EXT_CALL zend_jit_find_func_helper, r0
9525			} else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) {
9526				|	LOAD_ADDR FCARG1a, Z_STR_P(zv + 1);
9527				|	lea FCARG2a, aword [r2 + opline->result.num]
9528				|	EXT_CALL zend_jit_find_func_helper, r0
9529			} else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
9530				|	LOAD_ADDR FCARG1a, zv;
9531				|	lea FCARG2a, aword [r2 + opline->result.num]
9532				|	EXT_CALL zend_jit_find_ns_func_helper, r0
9533			} else {
9534				ZEND_UNREACHABLE();
9535			}
9536			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9537				int32_t exit_point = zend_jit_trace_get_exit_point(opline,
9538					func && (func->common.fn_flags & ZEND_ACC_IMMUTABLE) ? ZEND_JIT_EXIT_INVALIDATE : 0);
9539				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9540
9541				if (!exit_addr) {
9542					return 0;
9543				}
9544				if (!func || opline->opcode == ZEND_INIT_FCALL) {
9545					|	test r0, r0
9546					|	jnz >3
9547				} else if (func->type == ZEND_USER_FUNCTION
9548					 && !(func->common.fn_flags & ZEND_ACC_IMMUTABLE)) {
9549					const zend_op *opcodes = func->op_array.opcodes;
9550
9551					|   .if X64
9552					||		if (!IS_SIGNED_32BIT(opcodes)) {
9553					|			mov64 r1, ((ptrdiff_t)opcodes)
9554					|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], r1
9555					||		} else {
9556					|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9557					||		}
9558					|	.else
9559					|		cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9560					|	.endif
9561					|	jz >3
9562				} else {
9563					|   .if X64
9564					||		if (!IS_SIGNED_32BIT(func)) {
9565					|			mov64 r1, ((ptrdiff_t)func)
9566					|			cmp r0, r1
9567					||		} else {
9568					|			cmp r0, func
9569					||		}
9570					|	.else
9571					|		cmp r0, func
9572					|	.endif
9573					|	jz >3
9574				}
9575				|	jmp &exit_addr
9576			} else {
9577				|	test r0, r0
9578				|	jnz >3
9579				|	// SAVE_OPLINE();
9580				|	SET_EX_OPLINE opline, r0
9581				|	jmp ->undefined_function
9582			}
9583		}
9584		|.code
9585		|3:
9586	}
9587
9588	if (!zend_jit_push_call_frame(Dst, opline, op_array, func, 0, 0, checked_stack)) {
9589		return 0;
9590	}
9591
9592	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9593		if (!zend_jit_save_call_chain(Dst, call_level)) {
9594			return 0;
9595		}
9596	} else {
9597		delayed_call_chain = 1;
9598		delayed_call_level = call_level;
9599	}
9600
9601	return 1;
9602}
9603
9604static int zend_jit_init_method_call(dasm_State          **Dst,
9605                                     const zend_op        *opline,
9606                                     uint32_t              b,
9607                                     const zend_op_array  *op_array,
9608                                     zend_ssa             *ssa,
9609                                     const zend_ssa_op    *ssa_op,
9610                                     int                   call_level,
9611                                     uint32_t              op1_info,
9612                                     zend_jit_addr         op1_addr,
9613                                     zend_class_entry     *ce,
9614                                     bool                  ce_is_instanceof,
9615                                     bool                  on_this,
9616                                     bool                  delayed_fetch_this,
9617                                     zend_class_entry     *trace_ce,
9618                                     zend_jit_trace_rec   *trace,
9619                                     int                   checked_stack,
9620                                     bool                  polymorphic_side_trace)
9621{
9622	zend_func_info *info = ZEND_FUNC_INFO(op_array);
9623	zend_call_info *call_info = NULL;
9624	zend_function *func = NULL;
9625	zval *function_name;
9626
9627	ZEND_ASSERT(opline->op2_type == IS_CONST);
9628	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
9629
9630	function_name = RT_CONSTANT(opline, opline->op2);
9631
9632	if (info) {
9633		call_info = info->callee_info;
9634		while (call_info && call_info->caller_init_opline != opline) {
9635			call_info = call_info->next_callee;
9636		}
9637		if (call_info && call_info->callee_func && !call_info->is_prototype) {
9638			func = call_info->callee_func;
9639		}
9640	}
9641
9642	if (polymorphic_side_trace) {
9643		/* function is passed in r0 from parent_trace */
9644	} else {
9645		if (on_this) {
9646			zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
9647
9648			|	GET_ZVAL_PTR FCARG1a, this_addr
9649		} else {
9650		    if (op1_info & MAY_BE_REF) {
9651				if (opline->op1_type == IS_CV) {
9652					if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
9653						|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
9654					}
9655					|	ZVAL_DEREF FCARG1a, op1_info
9656					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
9657				} else {
9658					/* Hack: Convert reference to regular value to simplify JIT code */
9659					ZEND_ASSERT(Z_REG(op1_addr) == ZREG_FP);
9660					|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
9661					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
9662					|	EXT_CALL zend_jit_unref_helper, r0
9663					|1:
9664				}
9665			}
9666			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
9667				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9668					int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9669					const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9670
9671					if (!exit_addr) {
9672						return 0;
9673					}
9674					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
9675				} else {
9676					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
9677					|.cold_code
9678					|1:
9679					if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
9680						|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
9681					}
9682					|	SET_EX_OPLINE opline, r0
9683					if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9684						|	EXT_CALL zend_jit_invalid_method_call_tmp, r0
9685					} else {
9686						|	EXT_CALL zend_jit_invalid_method_call, r0
9687					}
9688					|	jmp ->exception_handler
9689					|.code
9690				}
9691			}
9692			|	GET_ZVAL_PTR FCARG1a, op1_addr
9693		}
9694
9695		if (delayed_call_chain) {
9696			if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9697				return 0;
9698			}
9699		}
9700
9701		|	mov aword T1, FCARG1a // save
9702
9703		if (func) {
9704			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
9705			|	mov r0, EX->run_time_cache
9706			|	mov r0, aword [r0 + opline->result.num + sizeof(void*)]
9707			|	test r0, r0
9708			|	jz >1
9709		} else {
9710			|	// if (CACHED_PTR(opline->result.num) == obj->ce)) {
9711			|	mov r0, EX->run_time_cache
9712			|	mov r2, aword [r0 + opline->result.num]
9713			|	cmp r2, [FCARG1a + offsetof(zend_object, ce)]
9714			|	jnz >1
9715			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
9716			|	mov r0, aword [r0 + opline->result.num + sizeof(void*)]
9717		}
9718
9719		|.cold_code
9720		|1:
9721		|	LOAD_ADDR FCARG2a, function_name
9722		|.if X64
9723		|	lea CARG3, aword T1
9724		|.else
9725		|	lea r0, aword T1
9726		|	sub r4, 12
9727		|	push r0
9728		|.endif
9729		|	SET_EX_OPLINE opline, r0
9730		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9731			|	EXT_CALL zend_jit_find_method_tmp_helper, r0
9732		} else {
9733			|	EXT_CALL zend_jit_find_method_helper, r0
9734		}
9735		|.if not(X64)
9736		|	add r4, 12
9737		|.endif
9738		|	test r0, r0
9739		|	jnz >2
9740		|	jmp ->exception_handler
9741		|.code
9742		|2:
9743	}
9744
9745	if ((!func || zend_jit_may_be_modified(func, op_array))
9746	 && trace
9747	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
9748	 && trace->func
9749#ifdef _WIN32
9750	 && trace->func->type != ZEND_INTERNAL_FUNCTION
9751#endif
9752	) {
9753		int32_t exit_point;
9754		const void *exit_addr;
9755
9756		exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : ZEND_JIT_EXIT_METHOD_CALL);
9757		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9758		if (!exit_addr) {
9759			return 0;
9760		}
9761
9762		func = (zend_function*)trace->func;
9763
9764		if (func->type == ZEND_USER_FUNCTION &&
9765		    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
9766		     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
9767		     !func->common.function_name)) {
9768			const zend_op *opcodes = func->op_array.opcodes;
9769
9770			|   .if X64
9771			||		if (!IS_SIGNED_32BIT(opcodes)) {
9772			|			mov64 r1, ((ptrdiff_t)opcodes)
9773			|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], r1
9774			||		} else {
9775			|			cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9776			||		}
9777			|	.else
9778			|		cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes
9779			|	.endif
9780			|	jne &exit_addr
9781		} else {
9782			|   .if X64
9783			||		if (!IS_SIGNED_32BIT(func)) {
9784			|			mov64 r1, ((ptrdiff_t)func)
9785			|			cmp r0, r1
9786			||		} else {
9787			|			cmp r0, func
9788			||		}
9789			|	.else
9790			|		cmp r0, func
9791			|	.endif
9792			|	jne &exit_addr
9793		}
9794	}
9795
9796	if (!func) {
9797		|	// if (fbc->common.fn_flags & ZEND_ACC_STATIC) {
9798		|	test dword [r0 + offsetof(zend_function, common.fn_flags)], ZEND_ACC_STATIC
9799		|	jnz >1
9800		|.cold_code
9801		|1:
9802	}
9803
9804	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) != 0) {
9805		|	mov FCARG1a, aword T1 // restore
9806		|	mov FCARG2a, r0
9807		|.if X64
9808		|	mov CARG3d, opline->extended_value
9809		|.else
9810		|	sub r4, 12
9811		|	push opline->extended_value
9812		|.endif
9813		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9814			|	EXT_CALL zend_jit_push_static_metod_call_frame_tmp, r0
9815		} else {
9816			|	EXT_CALL zend_jit_push_static_metod_call_frame, r0
9817		}
9818		|.if not(X64)
9819		|	add r4, 12
9820		|.endif
9821		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR) && !delayed_fetch_this)) {
9822			|	test r0, r0
9823			|	jz ->exception_handler
9824		}
9825		|	mov RX, r0
9826	}
9827
9828	if (!func) {
9829		|	jmp >9
9830		|.code
9831	}
9832
9833	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) == 0) {
9834		if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 0, delayed_fetch_this, checked_stack)) {
9835			return 0;
9836		}
9837	}
9838
9839	if (!func) {
9840		|9:
9841	}
9842	zend_jit_start_reuse_ip();
9843
9844	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9845		if (!zend_jit_save_call_chain(Dst, call_level)) {
9846			return 0;
9847		}
9848	} else {
9849		delayed_call_chain = 1;
9850		delayed_call_level = call_level;
9851	}
9852
9853	return 1;
9854}
9855
9856static int zend_jit_init_closure_call(dasm_State          **Dst,
9857                                      const zend_op        *opline,
9858                                      uint32_t              b,
9859                                      const zend_op_array  *op_array,
9860                                      zend_ssa             *ssa,
9861                                      const zend_ssa_op    *ssa_op,
9862                                      int                   call_level,
9863                                      zend_jit_trace_rec   *trace,
9864                                      int                   checked_stack)
9865{
9866	zend_function *func = NULL;
9867	zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
9868
9869	|	GET_ZVAL_PTR r0, op2_addr
9870
9871	if (ssa->var_info[ssa_op->op2_use].ce != zend_ce_closure
9872	 && !(ssa->var_info[ssa_op->op2_use].type & MAY_BE_CLASS_GUARD)) {
9873		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9874		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9875
9876		if (!exit_addr) {
9877			return 0;
9878		}
9879
9880		|.if X64
9881		||	if (!IS_SIGNED_32BIT(zend_ce_closure)) {
9882		|		mov64 FCARG1a, ((ptrdiff_t)zend_ce_closure)
9883		|		cmp aword [r0 + offsetof(zend_object, ce)], FCARG1a
9884		||	} else {
9885		|		cmp aword [r0 + offsetof(zend_object, ce)], zend_ce_closure
9886		||	}
9887		|.else
9888		|	cmp aword [r0 + offsetof(zend_object, ce)], zend_ce_closure
9889		|.endif
9890		|	jne &exit_addr
9891		if (ssa->var_info && ssa_op->op2_use >= 0) {
9892			ssa->var_info[ssa_op->op2_use].type |= MAY_BE_CLASS_GUARD;
9893			ssa->var_info[ssa_op->op2_use].ce = zend_ce_closure;
9894			ssa->var_info[ssa_op->op2_use].is_instanceof = 0;
9895		}
9896	}
9897
9898	if (trace
9899	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
9900	 && trace->func
9901	 && trace->func->type == ZEND_USER_FUNCTION) {
9902		const zend_op *opcodes;
9903		int32_t exit_point;
9904		const void *exit_addr;
9905
9906		func = (zend_function*)trace->func;
9907		opcodes = func->op_array.opcodes;
9908		exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_CLOSURE_CALL);
9909		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9910		if (!exit_addr) {
9911			return 0;
9912		}
9913
9914		|   .if X64
9915		||		if (!IS_SIGNED_32BIT(opcodes)) {
9916		|			mov64 FCARG1a, ((ptrdiff_t)opcodes)
9917		|			cmp aword [r0 + offsetof(zend_closure, func.op_array.opcodes)], FCARG1a
9918		||		} else {
9919		|			cmp aword [r0 + offsetof(zend_closure, func.op_array.opcodes)], opcodes
9920		||		}
9921		|	.else
9922		|		cmp aword [r0 + offsetof(zend_closure, func.op_array.opcodes)], opcodes
9923		|	.endif
9924		|	jne &exit_addr
9925	}
9926
9927	if (delayed_call_chain) {
9928		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9929			return 0;
9930		}
9931	}
9932
9933	if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 1, 0, checked_stack)) {
9934		return 0;
9935	}
9936
9937	if (zend_jit_needs_call_chain(NULL, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9938		if (!zend_jit_save_call_chain(Dst, call_level)) {
9939			return 0;
9940		}
9941	} else {
9942		delayed_call_chain = 1;
9943		delayed_call_level = call_level;
9944	}
9945
9946	if (trace
9947	 && trace->op == ZEND_JIT_TRACE_END
9948	 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
9949		if (!zend_jit_set_valid_ip(Dst, opline + 1)) {
9950			return 0;
9951		}
9952	}
9953
9954	return 1;
9955}
9956
9957static 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)
9958{
9959	zend_func_info *info = ZEND_FUNC_INFO(op_array);
9960	zend_call_info *call_info = NULL;
9961	const zend_function *func = NULL;
9962	uint32_t i;
9963	zend_jit_addr res_addr;
9964	uint32_t call_num_args = 0;
9965	bool unknown_num_args = 0;
9966	const void *exit_addr = NULL;
9967	const zend_op *prev_opline;
9968
9969	if (RETURN_VALUE_USED(opline)) {
9970		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
9971	} else {
9972		/* CPU stack allocated temporary zval */
9973		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R4, TMP_ZVAL_OFFSET);
9974	}
9975
9976	prev_opline = opline - 1;
9977	while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) {
9978		prev_opline--;
9979	}
9980	if (prev_opline->opcode == ZEND_SEND_UNPACK || prev_opline->opcode == ZEND_SEND_ARRAY ||
9981			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
9982		unknown_num_args = 1;
9983	}
9984
9985	if (info) {
9986		call_info = info->callee_info;
9987		while (call_info && call_info->caller_call_opline != opline) {
9988			call_info = call_info->next_callee;
9989		}
9990		if (call_info && call_info->callee_func && !call_info->is_prototype) {
9991			func = call_info->callee_func;
9992		}
9993		if ((op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)
9994		 && JIT_G(current_frame)
9995		 && JIT_G(current_frame)->call
9996		 && !JIT_G(current_frame)->call->func) {
9997			call_info = NULL; func = NULL; /* megamorphic call from trait */
9998		}
9999	}
10000	if (!func) {
10001		/* resolve function at run time */
10002	} else if (func->type == ZEND_USER_FUNCTION) {
10003		ZEND_ASSERT(opline->opcode != ZEND_DO_ICALL);
10004		call_num_args = call_info->num_args;
10005	} else if (func->type == ZEND_INTERNAL_FUNCTION) {
10006		ZEND_ASSERT(opline->opcode != ZEND_DO_UCALL);
10007		call_num_args = call_info->num_args;
10008	} else {
10009		ZEND_UNREACHABLE();
10010	}
10011
10012	if (trace && !func) {
10013		if (trace->op == ZEND_JIT_TRACE_DO_ICALL) {
10014			ZEND_ASSERT(trace->func->type == ZEND_INTERNAL_FUNCTION);
10015#ifndef ZEND_WIN32
10016			// TODO: ASLR may cause different addresses in different workers ???
10017			func = trace->func;
10018			if (JIT_G(current_frame) &&
10019			    JIT_G(current_frame)->call &&
10020			    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
10021				call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
10022			} else {
10023				unknown_num_args = 1;
10024			}
10025#endif
10026		} else if (trace->op == ZEND_JIT_TRACE_ENTER) {
10027			ZEND_ASSERT(trace->func->type == ZEND_USER_FUNCTION);
10028			if (zend_accel_in_shm(trace->func->op_array.opcodes)) {
10029				func = trace->func;
10030				if (JIT_G(current_frame) &&
10031				    JIT_G(current_frame)->call &&
10032				    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
10033					call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
10034				} else {
10035					unknown_num_args = 1;
10036				}
10037			}
10038		}
10039	}
10040
10041	bool may_have_extra_named_params =
10042		opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS &&
10043		(!func || func->common.fn_flags & ZEND_ACC_VARIADIC);
10044
10045	if (!reuse_ip) {
10046		zend_jit_start_reuse_ip();
10047		|	// call = EX(call);
10048		|	mov RX, EX->call
10049	}
10050	zend_jit_stop_reuse_ip();
10051
10052	|	// fbc = call->func;
10053	|	// mov r2, EX:RX->func ???
10054	|	// SAVE_OPLINE();
10055	|	SET_EX_OPLINE opline, r0
10056
10057	if (opline->opcode == ZEND_DO_FCALL) {
10058		if (!func) {
10059			if (trace) {
10060				uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10061
10062				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10063				if (!exit_addr) {
10064					return 0;
10065				}
10066				|	mov r0, EX:RX->func
10067				|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
10068				|	jnz &exit_addr
10069			}
10070		}
10071	}
10072
10073	if (!delayed_call_chain) {
10074		if (call_level == 1) {
10075			|	mov aword EX->call, 0
10076		} else {
10077			|	//EX(call) = call->prev_execute_data;
10078			|	mov r0, EX:RX->prev_execute_data
10079			|	mov EX->call, r0
10080		}
10081	}
10082	delayed_call_chain = 0;
10083
10084	|	//call->prev_execute_data = execute_data;
10085	|	mov EX:RX->prev_execute_data, EX
10086
10087	if (!func) {
10088		|	mov r0, EX:RX->func
10089	}
10090
10091	if (opline->opcode == ZEND_DO_FCALL) {
10092		if (!func) {
10093			if (!trace) {
10094				|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
10095				|	jnz >1
10096				|.cold_code
10097				|1:
10098				if (!GCC_GLOBAL_REGS) {
10099					|	mov FCARG1a, RX
10100				}
10101				|	EXT_CALL zend_jit_deprecated_helper, r0
10102				|	test al, al
10103				|	mov r0, EX:RX->func // reload
10104				|	jne >1
10105				|	jmp ->exception_handler
10106				|.code
10107				|1:
10108			}
10109		} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
10110			if (!GCC_GLOBAL_REGS) {
10111				|	mov FCARG1a, RX
10112			}
10113			|	EXT_CALL zend_jit_deprecated_helper, r0
10114			|	test al, al
10115			|	je ->exception_handler
10116		}
10117	}
10118
10119	if (!func
10120	 && opline->opcode != ZEND_DO_UCALL
10121	 && opline->opcode != ZEND_DO_ICALL) {
10122		|	cmp byte [r0 + offsetof(zend_function, type)], ZEND_USER_FUNCTION
10123		|	jne >8
10124	}
10125
10126	if ((!func || func->type == ZEND_USER_FUNCTION)
10127	 && opline->opcode != ZEND_DO_ICALL) {
10128		|	// EX(call) = NULL;
10129		|	mov aword EX:RX->call, 0
10130
10131		if (RETURN_VALUE_USED(opline)) {
10132			|	// EX(return_value) = EX_VAR(opline->result.var);
10133			|	LOAD_ZVAL_ADDR r2, res_addr
10134			|	mov aword EX:RX->return_value, r2
10135		} else {
10136			|	// EX(return_value) = 0;
10137			|	mov aword EX:RX->return_value, 0
10138		}
10139
10140		//EX_LOAD_RUN_TIME_CACHE(op_array);
10141		if (!func || func->op_array.cache_size) {
10142			if (func && op_array == &func->op_array) {
10143				/* recursive call */
10144				if (trace || func->op_array.cache_size > sizeof(void*)) {
10145					|	mov r2, EX->run_time_cache
10146					|	mov EX:RX->run_time_cache, r2
10147				}
10148			} else {
10149#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
10150				if (func) {
10151					|	mov r0, EX:RX->func
10152				}
10153				|	mov r2, aword [r0 + offsetof(zend_op_array, run_time_cache__ptr)]
10154				|	mov r2, aword [r2]
10155#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
10156				if (func && !(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
10157					if (ZEND_MAP_PTR_IS_OFFSET(func->op_array.run_time_cache)) {
10158						|	MEM_LOAD_ZTS r2, aword, compiler_globals, map_ptr_base, r1
10159						|	mov r2, aword [r2 + (uintptr_t)ZEND_MAP_PTR(func->op_array.run_time_cache)]
10160					} else if ((func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
10161					        && (!func->op_array.scope || (func->op_array.scope->ce_flags & ZEND_ACC_LINKED))) {
10162						if (func) {
10163							|	mov r0, EX:RX->func
10164						}
10165						|	mov r2, aword [r0 + offsetof(zend_op_array, run_time_cache__ptr)]
10166						|	MEM_LOAD_OP_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1
10167						|	mov r2, aword [r2]
10168					} else {
10169						/* the called op_array may be not persisted yet */
10170						if (func) {
10171							|	mov r0, EX:RX->func
10172						}
10173						|	mov r2, aword [r0 + offsetof(zend_op_array, run_time_cache__ptr)]
10174						|	test r2, 1
10175						|	jz >1
10176						|	MEM_LOAD_OP_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1
10177						|1:
10178						|	mov r2, aword [r2]
10179					}
10180				} else {
10181					if (func) {
10182						|	mov r0, EX:RX->func
10183					}
10184					|	mov r2, aword [r0 + offsetof(zend_op_array, run_time_cache__ptr)]
10185					|	test r2, 1
10186					|	jz >1
10187					|	MEM_LOAD_OP_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1
10188					|1:
10189					|	mov r2, aword [r2]
10190				}
10191#else
10192# error "Unknown ZEND_MAP_PTR_KIND"
10193#endif
10194				|	mov EX:RX->run_time_cache, r2
10195			}
10196		}
10197
10198		|	// EG(current_execute_data) = execute_data;
10199		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, RX, r1
10200		|	mov FP, RX
10201
10202		|	// opline = op_array->opcodes;
10203		if (func && !unknown_num_args) {
10204
10205			for (i = call_num_args; i < func->op_array.last_var; i++) {
10206				uint32_t n = EX_NUM_TO_VAR(i);
10207				|	SET_Z_TYPE_INFO RX + n, IS_UNDEF
10208			}
10209
10210			if (call_num_args <= func->op_array.num_args) {
10211				if (!trace || (trace->op == ZEND_JIT_TRACE_END
10212				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
10213					uint32_t num_args;
10214
10215					if ((func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
10216						if (trace) {
10217							num_args = 0;
10218						} else if (call_info) {
10219							num_args = skip_valid_arguments(op_array, ssa, call_info);
10220						} else {
10221							num_args = call_num_args;
10222						}
10223					} else {
10224						num_args = call_num_args;
10225					}
10226					if (zend_accel_in_shm(func->op_array.opcodes)) {
10227						|	LOAD_IP_ADDR (func->op_array.opcodes + num_args)
10228					} else {
10229						|	mov r0, EX->func
10230						if (GCC_GLOBAL_REGS) {
10231							|	mov IP, aword [r0 + offsetof(zend_op_array, opcodes)]
10232							if (num_args) {
10233								|	add IP, (num_args * sizeof(zend_op))
10234							}
10235						} else {
10236							|	mov FCARG1a, aword [r0 + offsetof(zend_op_array, opcodes)]
10237							if (num_args) {
10238								|	add FCARG1a, (num_args * sizeof(zend_op))
10239							}
10240							|	mov aword EX->opline, FCARG1a
10241						}
10242					}
10243
10244					if (GCC_GLOBAL_REGS && !trace && op_array == &func->op_array
10245							&& num_args >= op_array->required_num_args) {
10246						/* recursive call */
10247						if (ZEND_OBSERVER_ENABLED) {
10248							|	SAVE_IP
10249							|	mov FCARG1a, FP
10250							|	EXT_CALL zend_observer_fcall_begin, r0
10251						}
10252#ifdef CONTEXT_THREADED_JIT
10253						|	call >1
10254						|.cold_code
10255						|1:
10256						|	pop r0
10257						|	jmp =>num_args
10258						|.code
10259#else
10260						|	jmp =>num_args
10261#endif
10262						return 1;
10263					}
10264				}
10265			} else {
10266				if (!trace || (trace->op == ZEND_JIT_TRACE_END
10267				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
10268					if (func && zend_accel_in_shm(func->op_array.opcodes)) {
10269						|	LOAD_IP_ADDR (func->op_array.opcodes)
10270					} else if (GCC_GLOBAL_REGS) {
10271						|	mov IP, aword [r0 + offsetof(zend_op_array, opcodes)]
10272					} else {
10273						|	mov FCARG1a, aword [r0 + offsetof(zend_op_array, opcodes)]
10274						|	mov aword EX->opline, FCARG1a
10275					}
10276				}
10277				if (!GCC_GLOBAL_REGS) {
10278					|	mov FCARG1a, FP
10279				}
10280				|	EXT_CALL zend_jit_copy_extra_args_helper, r0
10281			}
10282		} else {
10283			|	// opline = op_array->opcodes
10284			if (func && zend_accel_in_shm(func->op_array.opcodes)) {
10285				|	LOAD_IP_ADDR (func->op_array.opcodes)
10286			} else if (GCC_GLOBAL_REGS) {
10287				|	mov IP, aword [r0 + offsetof(zend_op_array, opcodes)]
10288			} else {
10289				|	mov FCARG1a, aword [r0 + offsetof(zend_op_array, opcodes)]
10290				|	mov aword EX->opline, FCARG1a
10291			}
10292			if (func) {
10293				|	// num_args = EX_NUM_ARGS();
10294				|	mov ecx, dword [FP + offsetof(zend_execute_data, This.u2.num_args)]
10295				|	// if (UNEXPECTED(num_args > first_extra_arg))
10296				|	cmp ecx, (func->op_array.num_args)
10297			} else {
10298				|	// first_extra_arg = op_array->num_args;
10299				|	mov edx, dword [r0 + offsetof(zend_op_array, num_args)]
10300				|	// num_args = EX_NUM_ARGS();
10301				|	mov ecx, dword [FP + offsetof(zend_execute_data, This.u2.num_args)]
10302				|	// if (UNEXPECTED(num_args > first_extra_arg))
10303				|	cmp ecx, edx
10304			}
10305			|	jg >1
10306			|.cold_code
10307			|1:
10308			if (!GCC_GLOBAL_REGS) {
10309				|	mov FCARG1a, FP
10310			}
10311			|	EXT_CALL zend_jit_copy_extra_args_helper, r0
10312			if (!func) {
10313				|	mov r0, EX->func // reload
10314			}
10315			|	mov ecx, dword [FP + offsetof(zend_execute_data, This.u2.num_args)] // reload
10316			|	jmp >1
10317			|.code
10318			if (!func || (func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0) {
10319				if (!func) {
10320					|	// if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0))
10321					|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_HAS_TYPE_HINTS
10322					|	jnz >1
10323				}
10324				|	// opline += num_args;
10325				|.if X64
10326					||	ZEND_ASSERT(sizeof(zend_op) == 32);
10327					|	mov edx, ecx
10328					|	shl r2, 5
10329				|.else
10330					|	imul r2, ecx, sizeof(zend_op)
10331				|.endif
10332				|	ADD_IP r2
10333			}
10334			|1:
10335			|	// if (EXPECTED((int)num_args < op_array->last_var)) {
10336			if (func) {
10337				|	mov edx, (func->op_array.last_var)
10338			} else {
10339				|	mov edx, dword [r0 + offsetof(zend_op_array, last_var)]
10340			}
10341			|	sub edx, ecx
10342			|	jle >3 //???
10343			|	// zval *var = EX_VAR_NUM(num_args);
10344//			|.if X64
10345//				|	movsxd r1, ecx
10346//			|.endif
10347			|	shl r1, 4
10348			|	lea r1, [FP + r1 + (ZEND_CALL_FRAME_SLOT * sizeof(zval))]
10349			|2:
10350			|	SET_Z_TYPE_INFO r1, IS_UNDEF
10351			|	sub edx, 1
10352			|	lea r1, [r1 + 16]
10353			|	jne <2
10354			|3:
10355		}
10356
10357		if (ZEND_OBSERVER_ENABLED) {
10358			|	SAVE_IP
10359			|	mov FCARG1a, FP
10360			|	EXT_CALL zend_observer_fcall_begin, r0
10361		}
10362
10363		if (trace) {
10364			if (!func && (opline->opcode != ZEND_DO_UCALL)) {
10365				|	jmp >9
10366			}
10367		} else {
10368#ifdef CONTEXT_THREADED_JIT
10369			|	call ->context_threaded_call
10370			if (!func && (opline->opcode != ZEND_DO_UCALL)) {
10371				|	jmp >9
10372			}
10373			|	call ->context_threaded_call
10374			if (!func) {
10375				|	jmp >9
10376			}
10377#else
10378			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
10379				|	ADD_HYBRID_SPAD
10380				|	JMP_IP
10381			} else if (GCC_GLOBAL_REGS) {
10382				|	add r4, SPAD // stack alignment
10383				|	JMP_IP
10384			} else {
10385				|	mov FP, aword T2 // restore FP
10386				|	mov RX, aword T3 // restore IP
10387				|	add r4, NR_SPAD // stack alignment
10388				|	mov r0, 1 // ZEND_VM_ENTER
10389				|	ret
10390			}
10391		}
10392#endif
10393	}
10394
10395	if ((!func || func->type == ZEND_INTERNAL_FUNCTION)
10396	 && (opline->opcode != ZEND_DO_UCALL)) {
10397		if (!func && (opline->opcode != ZEND_DO_ICALL)) {
10398			|8:
10399		}
10400		if (opline->opcode == ZEND_DO_FCALL_BY_NAME) {
10401			if (!func) {
10402				if (trace) {
10403					uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10404
10405					exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10406					if (!exit_addr) {
10407						return 0;
10408					}
10409					|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
10410					|	jnz &exit_addr
10411				} else {
10412					|	test dword [r0 + offsetof(zend_op_array, fn_flags)], ZEND_ACC_DEPRECATED
10413					|	jnz >1
10414						|.cold_code
10415					|1:
10416					if (!GCC_GLOBAL_REGS) {
10417						|	mov FCARG1a, RX
10418					}
10419					|	EXT_CALL zend_jit_deprecated_helper, r0
10420					|	test al, al
10421					|	mov r0, EX:RX->func // reload
10422					|	jne >1
10423					|	jmp ->exception_handler
10424					|.code
10425					|1:
10426				}
10427			} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
10428				if (!GCC_GLOBAL_REGS) {
10429					|	mov FCARG1a, RX
10430				}
10431				|	EXT_CALL zend_jit_deprecated_helper, r0
10432				|	test al, al
10433				|	je ->exception_handler
10434				|	mov r0, EX:RX->func // reload
10435			}
10436		}
10437
10438		|	// ZVAL_NULL(EX_VAR(opline->result.var));
10439		|	LOAD_ZVAL_ADDR FCARG2a, res_addr
10440		|	SET_Z_TYPE_INFO FCARG2a, IS_NULL
10441
10442		|	// EG(current_execute_data) = execute_data;
10443		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, RX, r1
10444
10445		zend_jit_reset_last_valid_opline();
10446
10447		|	// (zend_execute_internal ? zend_execute_internal : fbc->internal_function.handler)(call, ret);
10448		if (zend_execute_internal) {
10449			|.if X64
10450				| // CARG2 and FCARG2a are identical
10451				|	mov CARG1, RX
10452			|.else
10453				|	mov aword A2, FCARG2a
10454				|	mov aword A1, RX
10455			|.endif
10456			|	EXT_CALL zend_execute_internal, r0
10457		} else {
10458			|	mov FCARG1a, RX
10459			if (func) {
10460				|	EXT_CALL func->internal_function.handler, r0
10461			} else {
10462				|	call aword [r0 + offsetof(zend_internal_function, handler)]
10463			}
10464		}
10465
10466		|	// EG(current_execute_data) = execute_data;
10467		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, FP, r0
10468
10469		|	// zend_vm_stack_free_args(call);
10470		if (func && !unknown_num_args) {
10471			for (i = 0; i < call_num_args; i++ ) {
10472				if (zend_jit_needs_arg_dtor(func, i, call_info)) {
10473					uint32_t offset = EX_NUM_TO_VAR(i);
10474					|	ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_RX, offset), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 0, 1, opline
10475				}
10476			}
10477		} else {
10478			|	mov FCARG1a, RX
10479			|	EXT_CALL zend_jit_vm_stack_free_args_helper, r0
10480		}
10481		if (may_have_extra_named_params) {
10482			|	test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS >> 24)
10483			|	jnz >1
10484			|.cold_code
10485			|1:
10486			|	mov FCARG1a, aword [RX + offsetof(zend_execute_data, extra_named_params)]
10487			|	EXT_CALL zend_free_extra_named_params, r0
10488			|	jmp >2
10489			|.code
10490			|2:
10491		}
10492
10493		|8:
10494		if (opline->opcode == ZEND_DO_FCALL) {
10495			// TODO: optimize ???
10496			|	// if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS))
10497			|	test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 2], (ZEND_CALL_RELEASE_THIS >> 16)
10498			|	jnz >1
10499			|.cold_code
10500			|1:
10501			|	GET_Z_PTR FCARG1a, RX + offsetof(zend_execute_data, This)
10502			|	// OBJ_RELEASE(object);
10503			|	OBJ_RELEASE ZREG_FCARG1, >2
10504			|	jmp >2
10505			|.code
10506			|2:
10507		}
10508
10509		if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
10510		    !JIT_G(current_frame) ||
10511		    !JIT_G(current_frame)->call ||
10512		    !TRACE_FRAME_IS_NESTED(JIT_G(current_frame)->call) ||
10513		    prev_opline->opcode == ZEND_SEND_UNPACK ||
10514		    prev_opline->opcode == ZEND_SEND_ARRAY ||
10515			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
10516
10517			|	// zend_vm_stack_free_call_frame(call);
10518			|	test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 2], (ZEND_CALL_ALLOCATED >> 16)
10519			|	jnz >1
10520			|.cold_code
10521			|1:
10522			|	mov FCARG1a, RX
10523			|	EXT_CALL zend_jit_free_call_frame, r0
10524			|	jmp >1
10525			|.code
10526		}
10527		|	MEM_STORE_ZTS aword, executor_globals, vm_stack_top, RX, r0
10528		|1:
10529
10530		if (!RETURN_VALUE_USED(opline)) {
10531			zend_class_entry *ce;
10532			bool ce_is_instanceof;
10533			uint32_t func_info = call_info ?
10534				zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof) :
10535				(MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
10536
10537			/* If an exception is thrown, the return_value may stay at the
10538			 * original value of null. */
10539			func_info |= MAY_BE_NULL;
10540
10541			if (func_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
10542				|	ZVAL_PTR_DTOR res_addr, func_info, 1, 1, opline
10543			}
10544		}
10545
10546		|	// if (UNEXPECTED(EG(exception) != NULL)) {
10547		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
10548		|	jne ->icall_throw_handler
10549
10550		// TODO: Can we avoid checking for interrupts after each call ???
10551		if (trace && last_valid_opline != opline) {
10552			int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM);
10553
10554			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10555			if (!exit_addr) {
10556				return 0;
10557			}
10558		} else {
10559			exit_addr = NULL;
10560		}
10561		if (!zend_jit_check_timeout(Dst, opline + 1, exit_addr)) {
10562			return 0;
10563		}
10564
10565		if ((!trace || !func) && opline->opcode != ZEND_DO_ICALL) {
10566			|	LOAD_IP_ADDR (opline + 1)
10567		} else if (trace
10568		 && trace->op == ZEND_JIT_TRACE_END
10569		 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
10570			|	LOAD_IP_ADDR (opline + 1)
10571		}
10572	}
10573
10574	if (!func) {
10575		|9:
10576	}
10577
10578	return 1;
10579}
10580
10581static int zend_jit_send_val(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr)
10582{
10583	uint32_t arg_num = opline->op2.num;
10584	zend_jit_addr arg_addr;
10585
10586	ZEND_ASSERT(opline->opcode == ZEND_SEND_VAL || arg_num <= MAX_ARG_FLAG_NUM);
10587
10588	if (!zend_jit_reuse_ip(Dst)) {
10589		return 0;
10590	}
10591
10592	if (opline->opcode == ZEND_SEND_VAL_EX) {
10593		uint32_t mask = ZEND_SEND_BY_REF << ((arg_num + 3) * 2);
10594
10595		ZEND_ASSERT(arg_num <= MAX_ARG_FLAG_NUM);
10596
10597		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10598		 && JIT_G(current_frame)
10599		 && JIT_G(current_frame)->call
10600		 && JIT_G(current_frame)->call->func) {
10601			if (ARG_MUST_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10602				/* Don't generate code that always throws exception */
10603				return 0;
10604			}
10605		} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10606			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10607			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10608			if (!exit_addr) {
10609				return 0;
10610			}
10611			|	mov r0, EX:RX->func
10612			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10613			|	jnz &exit_addr
10614		} else {
10615			|	mov r0, EX:RX->func
10616			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10617			|	jnz >1
10618			|.cold_code
10619			|1:
10620			if (Z_MODE(op1_addr) == IS_REG) {
10621				/* set type to avoid zval_ptr_dtor() on uninitialized value */
10622				zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
10623				|	SET_ZVAL_TYPE_INFO addr, IS_UNDEF
10624			}
10625			|	SET_EX_OPLINE opline, r0
10626			|	jmp ->throw_cannot_pass_by_ref
10627			|.code
10628
10629		}
10630	}
10631
10632	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
10633
10634	if (opline->op1_type == IS_CONST) {
10635		zval *zv = RT_CONSTANT(opline, opline->op1);
10636
10637		|	ZVAL_COPY_CONST arg_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_R0
10638		if (Z_REFCOUNTED_P(zv)) {
10639			|	ADDREF_CONST zv, r0
10640		}
10641	} else {
10642		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
10643	}
10644
10645	return 1;
10646}
10647
10648static int zend_jit_check_undef_args(dasm_State **Dst, const zend_op *opline)
10649{
10650	|	mov FCARG1a, EX->call
10651	|	test byte [FCARG1a + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_MAY_HAVE_UNDEF >> 24)
10652	|	jnz >1
10653	|.cold_code
10654	|1:
10655	|	SET_EX_OPLINE opline, r0
10656	|	EXT_CALL zend_handle_undef_args, r0
10657	|	test r0, r0
10658	|	jnz ->exception_handler
10659	|	jmp >2
10660	|.code
10661	|2:
10662
10663	return 1;
10664}
10665
10666static int zend_jit_send_ref(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, int cold)
10667{
10668	zend_jit_addr op1_addr, arg_addr, ref_addr;
10669
10670	op1_addr = OP1_ADDR();
10671	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
10672
10673	if (!zend_jit_reuse_ip(Dst)) {
10674		return 0;
10675	}
10676
10677	if (opline->op1_type == IS_VAR) {
10678		if (op1_info & MAY_BE_INDIRECT) {
10679			|	LOAD_ZVAL_ADDR r0, op1_addr
10680			|	// if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) {
10681			|	IF_NOT_Z_TYPE r0, IS_INDIRECT, >1
10682			|	// ret = Z_INDIRECT_P(ret);
10683			|	GET_Z_PTR r0, r0
10684			|1:
10685			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
10686		}
10687	} else if (opline->op1_type == IS_CV) {
10688		if (op1_info & MAY_BE_UNDEF) {
10689			if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10690				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
10691				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL
10692				|	jmp >2
10693				|1:
10694			}
10695			op1_info &= ~MAY_BE_UNDEF;
10696			op1_info |= MAY_BE_NULL;
10697		}
10698	} else {
10699		ZEND_UNREACHABLE();
10700	}
10701
10702	if (op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) {
10703		if (op1_info & MAY_BE_REF) {
10704			|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >2
10705			|	GET_ZVAL_PTR r1, op1_addr
10706			|	GC_ADDREF r1
10707			|	SET_ZVAL_PTR arg_addr, r1
10708			|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX
10709			|	jmp >6
10710		}
10711		|2:
10712		|	// ZVAL_NEW_REF(arg, varptr);
10713		if (opline->op1_type == IS_VAR) {
10714			if (Z_REG(op1_addr) != ZREG_R0 || Z_OFFSET(op1_addr) != 0) {
10715				|	LOAD_ZVAL_ADDR r0, op1_addr
10716			}
10717			|	mov aword T1, r0 // save
10718		}
10719		|	EMALLOC sizeof(zend_reference), op_array, opline
10720		|	mov dword [r0], 2
10721		|	mov dword [r0 + offsetof(zend_reference, gc.u.type_info)], GC_REFERENCE
10722		|	mov aword [r0 + offsetof(zend_reference, sources.ptr)], 0
10723		ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, offsetof(zend_reference, val));
10724		if (opline->op1_type == IS_VAR) {
10725			zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R1, 0);
10726
10727			|	mov r1, aword T1 // restore
10728			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_R2, ZREG_R2
10729			|	SET_ZVAL_PTR val_addr, r0
10730			|	SET_ZVAL_TYPE_INFO val_addr, IS_REFERENCE_EX
10731		} else {
10732			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10733			|	SET_ZVAL_PTR op1_addr, r0
10734			|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX
10735		}
10736		|	SET_ZVAL_PTR arg_addr, r0
10737		|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX
10738	}
10739
10740	|6:
10741	|	FREE_OP opline->op1_type, opline->op1, op1_info, !cold, opline
10742	|7:
10743
10744	return 1;
10745}
10746
10747static 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)
10748{
10749	uint32_t arg_num = opline->op2.num;
10750	zend_jit_addr arg_addr;
10751
10752	ZEND_ASSERT((opline->opcode != ZEND_SEND_VAR_EX &&
10753	     opline->opcode != ZEND_SEND_VAR_NO_REF_EX) ||
10754	    arg_num <= MAX_ARG_FLAG_NUM);
10755
10756	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
10757
10758	if (!zend_jit_reuse_ip(Dst)) {
10759		return 0;
10760	}
10761
10762	if (opline->opcode == ZEND_SEND_VAR_EX) {
10763		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10764		 && JIT_G(current_frame)
10765		 && JIT_G(current_frame)->call
10766		 && JIT_G(current_frame)->call->func) {
10767			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10768				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
10769					return 0;
10770				}
10771				return 1;
10772			}
10773		} else {
10774			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10775
10776			|	mov r0, EX:RX->func
10777			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10778			|	jnz >1
10779			|.cold_code
10780			|1:
10781			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
10782				return 0;
10783			}
10784			|	jmp >7
10785			|.code
10786		}
10787	} else if (opline->opcode == ZEND_SEND_VAR_NO_REF_EX) {
10788		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10789		 && JIT_G(current_frame)
10790		 && JIT_G(current_frame)->call
10791		 && JIT_G(current_frame)->call->func) {
10792			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10793
10794				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10795
10796				if (!ARG_MAY_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10797					if (!(op1_info & MAY_BE_REF)) {
10798						/* Don't generate code that always throws exception */
10799						return 0;
10800					} else {
10801						int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10802						const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10803						if (!exit_addr) {
10804							return 0;
10805						}
10806						|	cmp cl, IS_REFERENCE
10807						|	jne &exit_addr
10808					}
10809				}
10810				return 1;
10811			}
10812		} else {
10813			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10814
10815			|	mov r0, EX:RX->func
10816			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10817			|	jnz >1
10818			|.cold_code
10819			|1:
10820
10821			mask = ZEND_SEND_PREFER_REF << ((arg_num + 3) * 2);
10822
10823			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10824			if (op1_info & MAY_BE_REF) {
10825				|	cmp cl, IS_REFERENCE
10826				|	je >7
10827			}
10828			|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
10829			|	jnz >7
10830			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10831				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10832				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10833				if (!exit_addr) {
10834					return 0;
10835				}
10836				|	jmp &exit_addr
10837			} else {
10838				|	SET_EX_OPLINE opline, r0
10839				|	LOAD_ZVAL_ADDR FCARG1a, arg_addr
10840				|	EXT_CALL zend_jit_only_vars_by_reference, r0
10841				if (!zend_jit_check_exception(Dst)) {
10842					return 0;
10843				}
10844				|	jmp >7
10845			}
10846
10847			|.code
10848		}
10849	} else if (opline->opcode == ZEND_SEND_FUNC_ARG) {
10850		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10851		 && JIT_G(current_frame)
10852		 && JIT_G(current_frame)->call
10853		 && JIT_G(current_frame)->call->func) {
10854			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10855				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
10856					return 0;
10857				}
10858				return 1;
10859			}
10860		} else {
10861			|	test dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
10862			|	jnz >1
10863			|.cold_code
10864			|1:
10865			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
10866				return 0;
10867			}
10868			|	jmp >7
10869			|.code
10870		}
10871	}
10872
10873	if (op1_info & MAY_BE_UNDEF) {
10874		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10875			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1
10876			|.cold_code
10877			|1:
10878		}
10879
10880		|	SET_EX_OPLINE opline, r0
10881		|	mov FCARG1d, opline->op1.var
10882		|	EXT_CALL zend_jit_undefined_op_helper, r0
10883		|	SET_ZVAL_TYPE_INFO arg_addr, IS_NULL
10884		|	test r0, r0
10885		|	jz ->exception_handler
10886
10887		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10888			|	jmp >7
10889			|.code
10890		} else {
10891			|7:
10892			return 1;
10893		}
10894	}
10895
10896	if (opline->opcode == ZEND_SEND_VAR_NO_REF) {
10897		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R1, ZREG_R2
10898		if (op1_info & MAY_BE_REF) {
10899			|	cmp cl, IS_REFERENCE
10900			|	je >7
10901		}
10902		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10903			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10904			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10905			if (!exit_addr) {
10906				return 0;
10907			}
10908			|	jmp &exit_addr
10909		} else {
10910			|	SET_EX_OPLINE opline, r0
10911			|	LOAD_ZVAL_ADDR FCARG1a, arg_addr
10912			|	EXT_CALL zend_jit_only_vars_by_reference, r0
10913			if (!zend_jit_check_exception(Dst)) {
10914				return 0;
10915			}
10916		}
10917	} else {
10918		if (op1_info & MAY_BE_REF) {
10919			if (opline->op1_type == IS_CV) {
10920				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
10921
10922				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
10923				|	ZVAL_DEREF FCARG1a, op1_info
10924				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_R0, ZREG_R2
10925				|	TRY_ADDREF op1_info, ah, r2
10926			} else {
10927				zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 8);
10928
10929				|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
10930				|.cold_code
10931				|1:
10932				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
10933				|	GET_ZVAL_PTR FCARG1a, op1_addr
10934				|	// ZVAL_COPY_VALUE(return_value, &ref->value);
10935				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_R0, ZREG_R2
10936				|	GC_DELREF FCARG1a
10937				|	je >1
10938				|	IF_NOT_REFCOUNTED ah, >2
10939				|	GC_ADDREF r2
10940				|	jmp >2
10941				|1:
10942				|	EFREE_REG_REFERENCE
10943				|	jmp >2
10944				|.code
10945				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
10946				|2:
10947			}
10948		} else {
10949			if (op1_addr != op1_def_addr) {
10950				if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
10951					return 0;
10952				}
10953				if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
10954					op1_addr= op1_def_addr;
10955				}
10956			}
10957			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
10958			if (opline->op1_type == IS_CV) {
10959				|	TRY_ADDREF op1_info, ah, r2
10960			}
10961		}
10962	}
10963	|7:
10964
10965	return 1;
10966}
10967
10968static int zend_jit_check_func_arg(dasm_State **Dst, const zend_op *opline)
10969{
10970	uint32_t arg_num = opline->op2.num;
10971
10972	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10973	 && JIT_G(current_frame)
10974	 && JIT_G(current_frame)->call
10975	 && JIT_G(current_frame)->call->func) {
10976		if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10977			if (!TRACE_FRAME_IS_LAST_SEND_BY_REF(JIT_G(current_frame)->call)) {
10978				TRACE_FRAME_SET_LAST_SEND_BY_REF(JIT_G(current_frame)->call);
10979				|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10980				||	if (reuse_ip) {
10981				|		or dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
10982				||	} else {
10983				|		mov r0, EX->call
10984				|		or dword [r0 + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
10985				||	}
10986			}
10987		} else {
10988			if (!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
10989				TRACE_FRAME_SET_LAST_SEND_BY_VAL(JIT_G(current_frame)->call);
10990				|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10991				||	if (reuse_ip) {
10992				|		and dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
10993				||	} else {
10994				|		mov r0, EX->call
10995				|		and dword [r0 + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
10996				||	}
10997			}
10998		}
10999	} else {
11000		// if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
11001		uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
11002
11003		if (!zend_jit_reuse_ip(Dst)) {
11004			return 0;
11005		}
11006
11007		|	mov r0, EX:RX->func
11008		|	test dword [r0 + offsetof(zend_function, quick_arg_flags)], mask
11009		|	jnz >1
11010		|.cold_code
11011		|1:
11012		|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
11013		|	or dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ZEND_CALL_SEND_ARG_BY_REF
11014		|	jmp >1
11015		|.code
11016		|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
11017		|	and dword [RX + offsetof(zend_execute_data, This.u1.type_info)], ~ZEND_CALL_SEND_ARG_BY_REF
11018		|1:
11019	}
11020
11021	return 1;
11022}
11023
11024static int zend_jit_smart_true(dasm_State **Dst, const zend_op *opline, int jmp, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2)
11025{
11026	if (smart_branch_opcode) {
11027		if (smart_branch_opcode == ZEND_JMPZ) {
11028			if (jmp) {
11029				|	jmp >7
11030			}
11031		} else if (smart_branch_opcode == ZEND_JMPNZ) {
11032			|	jmp =>target_label
11033		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11034			|	jmp =>target_label2
11035		} else {
11036			ZEND_UNREACHABLE();
11037		}
11038	} else {
11039		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11040
11041		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
11042		if (jmp) {
11043			|	jmp >7
11044		}
11045	}
11046
11047	return 1;
11048}
11049
11050static int zend_jit_smart_false(dasm_State **Dst, const zend_op *opline, int jmp, zend_uchar smart_branch_opcode, uint32_t target_label)
11051{
11052	if (smart_branch_opcode) {
11053		if (smart_branch_opcode == ZEND_JMPZ) {
11054			|	jmp =>target_label
11055		} else if (smart_branch_opcode == ZEND_JMPNZ) {
11056			if (jmp) {
11057				|	jmp >7
11058			}
11059		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11060			|	jmp =>target_label
11061		} else {
11062			ZEND_UNREACHABLE();
11063		}
11064	} else {
11065		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11066
11067		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
11068		if (jmp) {
11069			|	jmp >7
11070		}
11071	}
11072
11073	return 1;
11074}
11075
11076static int zend_jit_defined(dasm_State **Dst, const zend_op *opline, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
11077{
11078	uint32_t defined_label = (uint32_t)-1;
11079	uint32_t undefined_label = (uint32_t)-1;
11080	zval *zv = RT_CONSTANT(opline, opline->op1);
11081	zend_jit_addr res_addr = 0;
11082
11083	if (smart_branch_opcode && !exit_addr) {
11084		if (smart_branch_opcode == ZEND_JMPZ) {
11085			undefined_label = target_label;
11086		} else if (smart_branch_opcode == ZEND_JMPNZ) {
11087			defined_label = target_label;
11088		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11089			undefined_label = target_label;
11090			defined_label = target_label2;
11091		} else {
11092			ZEND_UNREACHABLE();
11093		}
11094	}
11095
11096	|	// if (CACHED_PTR(opline->extended_value)) {
11097	|	mov r0, EX->run_time_cache
11098	|	mov r0, aword [r0 + opline->extended_value]
11099	|	test r0, r0
11100	|	jz >1
11101	|	test r0, 0x1
11102	|	jnz >4
11103	|.cold_code
11104	|4:
11105	|	MEM_LOAD_ZTS FCARG1a, aword, executor_globals, zend_constants, FCARG1a
11106	|	shr r0, 1
11107	|	cmp dword [FCARG1a + offsetof(HashTable, nNumOfElements)], eax
11108
11109	if (smart_branch_opcode) {
11110		if (exit_addr) {
11111			if (smart_branch_opcode == ZEND_JMPZ) {
11112				|	jz &exit_addr
11113			} else {
11114				|	jz >3
11115			}
11116		} else if (undefined_label != (uint32_t)-1) {
11117			|	jz =>undefined_label
11118		} else {
11119			|	jz >3
11120		}
11121	} else {
11122		|	jz >2
11123	}
11124	|1:
11125	|	SET_EX_OPLINE opline, r0
11126	|	LOAD_ADDR FCARG1a, zv
11127	|	EXT_CALL zend_jit_check_constant, r0
11128	|	test r0, r0
11129	if (exit_addr) {
11130		if (smart_branch_opcode == ZEND_JMPNZ) {
11131			|	jz >3
11132		} else {
11133			|	jnz >3
11134		}
11135		|	jmp &exit_addr
11136	} else if (smart_branch_opcode) {
11137		if (undefined_label != (uint32_t)-1) {
11138			|	jz =>undefined_label
11139		} else {
11140			|	jz >3
11141		}
11142		if (defined_label != (uint32_t)-1) {
11143			|	jmp =>defined_label
11144		} else {
11145			|	jmp >3
11146		}
11147	} else {
11148		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11149		|	jnz >1
11150		|2:
11151		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
11152		|	jmp >3
11153	}
11154	|.code
11155	if (smart_branch_opcode) {
11156		if (exit_addr) {
11157			if (smart_branch_opcode == ZEND_JMPNZ) {
11158				|	jmp &exit_addr
11159			}
11160		} else if (defined_label != (uint32_t)-1) {
11161			|	jmp =>defined_label
11162		}
11163	} else {
11164		|1:
11165		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
11166	}
11167	|3:
11168
11169	return 1;
11170}
11171
11172static int zend_jit_type_check(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
11173{
11174	uint32_t  mask;
11175	zend_jit_addr op1_addr = OP1_ADDR();
11176
11177	// TODO: support for is_resource() ???
11178	ZEND_ASSERT(opline->extended_value != MAY_BE_RESOURCE);
11179
11180	if (op1_info & MAY_BE_UNDEF) {
11181		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
11182			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1
11183			|.cold_code
11184			|1:
11185		}
11186		|	SET_EX_OPLINE opline, r0
11187		|	mov FCARG1d, opline->op1.var
11188		|	EXT_CALL zend_jit_undefined_op_helper, r0
11189		zend_jit_check_exception_undef_result(Dst, opline);
11190		if (opline->extended_value & MAY_BE_NULL) {
11191			if (exit_addr) {
11192				if (smart_branch_opcode == ZEND_JMPNZ) {
11193					|	jmp &exit_addr
11194				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
11195					|	jmp >7
11196				}
11197			} else if (!zend_jit_smart_true(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label, target_label2)) {
11198				return 0;
11199			}
11200		} else {
11201			if (exit_addr) {
11202				if (smart_branch_opcode == ZEND_JMPZ) {
11203					|	jmp &exit_addr
11204				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
11205					|	jmp >7
11206				}
11207			} else if (!zend_jit_smart_false(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label)) {
11208				return 0;
11209			}
11210		}
11211		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
11212			|.code
11213		}
11214	}
11215
11216	if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
11217		mask = opline->extended_value;
11218		if (!(op1_info & MAY_BE_GUARD) && !(op1_info & (MAY_BE_ANY - mask))) {
11219			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11220			if (exit_addr) {
11221				if (smart_branch_opcode == ZEND_JMPNZ) {
11222					|	jmp &exit_addr
11223				}
11224			} else if (!zend_jit_smart_true(Dst, opline, 0, smart_branch_opcode, target_label, target_label2)) {
11225				return 0;
11226			}
11227	    } else if (!(op1_info & MAY_BE_GUARD) && !(op1_info & mask)) {
11228			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11229			if (exit_addr) {
11230				if (smart_branch_opcode == ZEND_JMPZ) {
11231					|	jmp &exit_addr
11232				}
11233			} else if (!zend_jit_smart_false(Dst, opline, 0, smart_branch_opcode, target_label)) {
11234				return 0;
11235			}
11236		} else {
11237			bool invert = 0;
11238			zend_uchar type;
11239
11240			switch (mask) {
11241				case MAY_BE_NULL:   type = IS_NULL;   break;
11242				case MAY_BE_FALSE:  type = IS_FALSE;  break;
11243				case MAY_BE_TRUE:   type = IS_TRUE;   break;
11244				case MAY_BE_LONG:   type = IS_LONG;   break;
11245				case MAY_BE_DOUBLE: type = IS_DOUBLE; break;
11246				case MAY_BE_STRING: type = IS_STRING; break;
11247				case MAY_BE_ARRAY:  type = IS_ARRAY;  break;
11248				case MAY_BE_OBJECT: type = IS_OBJECT; break;
11249				case MAY_BE_ANY - MAY_BE_NULL:     type = IS_NULL;   invert = 1; break;
11250				case MAY_BE_ANY - MAY_BE_FALSE:    type = IS_FALSE;  invert = 1; break;
11251				case MAY_BE_ANY - MAY_BE_TRUE:     type = IS_TRUE;   invert = 1; break;
11252				case MAY_BE_ANY - MAY_BE_LONG:     type = IS_LONG;   invert = 1; break;
11253				case MAY_BE_ANY - MAY_BE_DOUBLE:   type = IS_DOUBLE; invert = 1; break;
11254				case MAY_BE_ANY - MAY_BE_STRING:   type = IS_STRING; invert = 1; break;
11255				case MAY_BE_ANY - MAY_BE_ARRAY:    type = IS_ARRAY;  invert = 1; break;
11256				case MAY_BE_ANY - MAY_BE_OBJECT:   type = IS_OBJECT; invert = 1; break;
11257				case MAY_BE_ANY - MAY_BE_RESOURCE: type = IS_OBJECT; invert = 1; break;
11258				default:
11259					type = 0;
11260			}
11261
11262			if (op1_info & MAY_BE_REF) {
11263				|	LOAD_ZVAL_ADDR r0, op1_addr
11264				|	ZVAL_DEREF r0, op1_info
11265			}
11266			if (type == 0) {
11267				if (smart_branch_opcode &&
11268				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
11269				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11270					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11271						|	// if (Z_REFCOUNTED_P(cv)) {
11272						|	IF_ZVAL_REFCOUNTED op1_addr, >1
11273						|.cold_code
11274						|1:
11275					}
11276					|	// if (!Z_DELREF_P(cv)) {
11277					|	GET_ZVAL_PTR FCARG1a, op1_addr
11278					|	GC_DELREF FCARG1a
11279					if (RC_MAY_BE_1(op1_info)) {
11280						if (RC_MAY_BE_N(op1_info)) {
11281							|	jnz >3
11282						}
11283						if (op1_info & MAY_BE_REF) {
11284							|	mov al, byte [r0 + 8]
11285						} else {
11286							|	mov al, byte [FP + opline->op1.var + 8]
11287						}
11288						|	mov byte T1, al // save
11289						|	// zval_dtor_func(r);
11290						|	ZVAL_DTOR_FUNC op1_info, opline
11291						|	mov cl, byte T1 // restore
11292						|jmp >2
11293					}
11294					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11295						if (!RC_MAY_BE_1(op1_info)) {
11296							|	jmp >3
11297						}
11298						|.code
11299					}
11300					|3:
11301					if (op1_info & MAY_BE_REF) {
11302						|	mov cl, byte [r0 + 8]
11303					} else {
11304						|	mov cl, byte [FP + opline->op1.var + 8]
11305					}
11306					|2:
11307				} else {
11308					if (op1_info & MAY_BE_REF) {
11309						|	mov cl, byte [r0 + 8]
11310					} else {
11311						|	mov cl, byte [FP + opline->op1.var + 8]
11312					}
11313				}
11314				|	mov eax, 1
11315				|	shl eax, cl
11316				|	test eax, mask
11317				if (exit_addr) {
11318					if (smart_branch_opcode == ZEND_JMPNZ) {
11319						|	jne &exit_addr
11320					} else {
11321						|	je &exit_addr
11322					}
11323				} else if (smart_branch_opcode) {
11324					if (smart_branch_opcode == ZEND_JMPZ) {
11325						|	je =>target_label
11326					} else if (smart_branch_opcode == ZEND_JMPNZ) {
11327						|	jne =>target_label
11328					} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11329						|	je =>target_label
11330						|	jmp =>target_label2
11331					} else {
11332						ZEND_UNREACHABLE();
11333					}
11334				} else {
11335					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11336
11337					|	setne al
11338					|	movzx eax, al
11339					|	add eax, 2
11340					|	SET_ZVAL_TYPE_INFO res_addr, eax
11341					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11342				}
11343			} else {
11344				if (smart_branch_opcode &&
11345				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
11346				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11347					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11348						|	// if (Z_REFCOUNTED_P(cv)) {
11349						|	IF_ZVAL_REFCOUNTED op1_addr, >1
11350						|.cold_code
11351						|1:
11352					}
11353					|	// if (!Z_DELREF_P(cv)) {
11354					|	GET_ZVAL_PTR FCARG1a, op1_addr
11355					|	GC_DELREF FCARG1a
11356					if (RC_MAY_BE_1(op1_info)) {
11357						if (RC_MAY_BE_N(op1_info)) {
11358							|	jnz >3
11359						}
11360						if (op1_info & MAY_BE_REF) {
11361							|	mov al, byte [r0 + 8]
11362						} else {
11363							|	mov al, byte [FP + opline->op1.var + 8]
11364						}
11365						|	mov byte T1, al // save
11366						|	// zval_dtor_func(r);
11367						|	ZVAL_DTOR_FUNC op1_info, opline
11368						|	mov cl, byte T1 // restore
11369						|jmp >2
11370					}
11371					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11372						if (!RC_MAY_BE_1(op1_info)) {
11373							|	jmp >3
11374						}
11375						|.code
11376					}
11377					|3:
11378					if (op1_info & MAY_BE_REF) {
11379						|	mov cl, byte [r0 + 8]
11380					} else {
11381						|	mov cl, byte [FP + opline->op1.var + 8]
11382					}
11383					|2:
11384					|	cmp cl, type
11385				} else {
11386					if (op1_info & MAY_BE_REF) {
11387						|	cmp byte [r0 + 8], type
11388					} else {
11389						|	cmp byte [FP + opline->op1.var + 8], type
11390					}
11391				}
11392				if (exit_addr) {
11393					if (invert) {
11394						if (smart_branch_opcode == ZEND_JMPNZ) {
11395							|	jne &exit_addr
11396						} else {
11397							|	je &exit_addr
11398						}
11399					} else {
11400						if (smart_branch_opcode == ZEND_JMPNZ) {
11401							|	je &exit_addr
11402						} else {
11403							|	jne &exit_addr
11404						}
11405					}
11406				} else if (smart_branch_opcode) {
11407					if (invert) {
11408						if (smart_branch_opcode == ZEND_JMPZ) {
11409							|	je =>target_label
11410						} else if (smart_branch_opcode == ZEND_JMPNZ) {
11411							|	jne =>target_label
11412						} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11413							|	je =>target_label
11414							|	jmp =>target_label2
11415						} else {
11416							ZEND_UNREACHABLE();
11417						}
11418					} else {
11419						if (smart_branch_opcode == ZEND_JMPZ) {
11420							|	jne =>target_label
11421						} else if (smart_branch_opcode == ZEND_JMPNZ) {
11422							|	je =>target_label
11423						} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11424							|	jne =>target_label
11425							|	jmp =>target_label2
11426						} else {
11427							ZEND_UNREACHABLE();
11428						}
11429					}
11430				} else {
11431					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11432
11433					if (invert) {
11434						|	setne al
11435					} else {
11436						|	sete al
11437					}
11438					|	movzx eax, al
11439					|	add eax, 2
11440					|	SET_ZVAL_TYPE_INFO res_addr, eax
11441					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
11442				}
11443			}
11444	    }
11445	}
11446
11447	|7:
11448
11449	return 1;
11450}
11451
11452static int zend_jit_leave_frame(dasm_State **Dst)
11453{
11454	|	// EG(current_execute_data) = EX(prev_execute_data);
11455	|	mov r0, EX->prev_execute_data
11456	|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, r0, r2
11457	return 1;
11458}
11459
11460static int zend_jit_free_cvs(dasm_State **Dst)
11461{
11462	|	// EG(current_execute_data) = EX(prev_execute_data);
11463	|	mov FCARG1a, EX->prev_execute_data
11464	|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, FCARG1a, r0
11465	|	// zend_free_compiled_variables(execute_data);
11466	|	mov FCARG1a, FP
11467	|	EXT_CALL zend_free_compiled_variables, r0
11468	return 1;
11469}
11470
11471static int zend_jit_free_cv(dasm_State **Dst, uint32_t info, uint32_t var)
11472{
11473	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
11474		uint32_t offset = EX_NUM_TO_VAR(var);
11475		| ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_FP, offset), info, 1, 1, NULL
11476	}
11477	return 1;
11478}
11479
11480static int zend_jit_free_op(dasm_State **Dst, const zend_op *opline, uint32_t info, uint32_t var_offset)
11481{
11482	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
11483		| ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_FP, var_offset), info, 0, 1, opline
11484	}
11485	return 1;
11486}
11487
11488static int zend_jit_leave_func(dasm_State          **Dst,
11489                               const zend_op_array  *op_array,
11490                               const zend_op        *opline,
11491                               uint32_t              op1_info,
11492                               bool             left_frame,
11493                               zend_jit_trace_rec   *trace,
11494                               zend_jit_trace_info  *trace_info,
11495                               int                   indirect_var_access,
11496                               int                   may_throw)
11497{
11498	bool may_be_top_frame =
11499		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11500		!JIT_G(current_frame) ||
11501		!TRACE_FRAME_IS_NESTED(JIT_G(current_frame));
11502	bool may_need_call_helper =
11503		indirect_var_access || /* may have symbol table */
11504		!op_array->function_name || /* may have symbol table */
11505		may_be_top_frame ||
11506		(op_array->fn_flags & ZEND_ACC_VARIADIC) || /* may have extra named args */
11507		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11508		!JIT_G(current_frame) ||
11509		TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) == -1 || /* unknown number of args */
11510		(uint32_t)TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) > op_array->num_args; /* extra args */
11511	bool may_need_release_this =
11512		!(op_array->fn_flags & ZEND_ACC_CLOSURE) &&
11513		op_array->scope &&
11514		!(op_array->fn_flags & ZEND_ACC_STATIC) &&
11515		(JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11516		 !JIT_G(current_frame) ||
11517		 !TRACE_FRAME_NO_NEED_RELEASE_THIS(JIT_G(current_frame)));
11518
11519	if (may_need_release_this) {
11520		|	mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)]
11521	}
11522	if (may_need_call_helper) {
11523		if (!left_frame) {
11524			left_frame = 1;
11525		    if (!zend_jit_leave_frame(Dst)) {
11526				return 0;
11527		    }
11528		}
11529		/* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */
11530		if (may_need_release_this) {
11531			|	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)
11532		} else {
11533			|	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)
11534		}
11535		if (trace && trace->op != ZEND_JIT_TRACE_END) {
11536			|	jnz >1
11537			|.cold_code
11538			|1:
11539			if (!GCC_GLOBAL_REGS) {
11540				|	mov FCARG1a, FP
11541			}
11542			|	EXT_CALL zend_jit_leave_func_helper, r0
11543
11544			if (may_be_top_frame) {
11545				// TODO: try to avoid this check ???
11546				if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
11547#if 0
11548					/* this check should be handled by the following OPLINE guard */
11549					|	cmp IP, zend_jit_halt_op
11550					|	je ->trace_halt
11551#endif
11552				} else if (GCC_GLOBAL_REGS) {
11553					|	test IP, IP
11554					|	je ->trace_halt
11555				} else {
11556					|	test eax, eax
11557					|	jl ->trace_halt
11558				}
11559			}
11560
11561			if (!GCC_GLOBAL_REGS) {
11562				|	// execute_data = EG(current_execute_data)
11563				|	MEM_LOAD_ZTS FP, aword, executor_globals, current_execute_data, r0
11564			}
11565			|	jmp >8
11566			|.code
11567		} else {
11568			|	jnz ->leave_function_handler
11569		}
11570	}
11571
11572	if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
11573		if (!left_frame) {
11574			left_frame = 1;
11575		    if (!zend_jit_leave_frame(Dst)) {
11576				return 0;
11577		    }
11578		}
11579		|	// OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
11580		|	mov FCARG1a, EX->func
11581		|	sub FCARG1a, sizeof(zend_object)
11582		|	OBJ_RELEASE ZREG_FCARG1, >4
11583		|4:
11584	} else if (may_need_release_this) {
11585		if (!left_frame) {
11586			left_frame = 1;
11587		    if (!zend_jit_leave_frame(Dst)) {
11588				return 0;
11589		    }
11590		}
11591		|	// if (call_info & ZEND_CALL_RELEASE_THIS)
11592		|	test FCARG1d, ZEND_CALL_RELEASE_THIS
11593		|	je >4
11594		|	// zend_object *object = Z_OBJ(execute_data->This);
11595		|	mov FCARG1a, EX->This.value.obj
11596		|	// OBJ_RELEASE(object);
11597		|	OBJ_RELEASE ZREG_FCARG1, >4
11598		|4:
11599		// TODO: avoid EG(excption) check for $this->foo() calls
11600		may_throw = 1;
11601	}
11602
11603	|	// EG(vm_stack_top) = (zval*)execute_data;
11604	|	MEM_STORE_ZTS aword, executor_globals, vm_stack_top, FP, r0
11605	|	// execute_data = EX(prev_execute_data);
11606	|	mov FP, EX->prev_execute_data
11607
11608	if (!left_frame) {
11609		|	// EG(current_execute_data) = execute_data;
11610		|	MEM_STORE_ZTS aword, executor_globals, current_execute_data, FP, r0
11611	}
11612
11613	|9:
11614	if (trace) {
11615		if (trace->op != ZEND_JIT_TRACE_END
11616		 && (JIT_G(current_frame) && !TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
11617			zend_jit_reset_last_valid_opline();
11618		} else {
11619			|	LOAD_IP
11620			|	ADD_IP sizeof(zend_op)
11621		}
11622
11623		|8:
11624
11625		if (trace->op == ZEND_JIT_TRACE_BACK
11626		 && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
11627			const zend_op *next_opline = trace->opline;
11628
11629			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
11630			 && (op1_info & MAY_BE_RC1)
11631			 && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
11632				/* exception might be thrown during destruction of unused return value */
11633				|	// if (EG(exception))
11634				|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
11635				|	jne ->leave_throw_handler
11636			}
11637			do {
11638				trace++;
11639			} while (trace->op == ZEND_JIT_TRACE_INIT_CALL);
11640			ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
11641			next_opline = trace->opline;
11642			ZEND_ASSERT(next_opline != NULL);
11643
11644			if (trace->op == ZEND_JIT_TRACE_END
11645			 && trace->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
11646				trace_info->flags |= ZEND_JIT_TRACE_LOOP;
11647				|	CMP_IP next_opline
11648				|	je =>0 // LOOP
11649#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
11650				|	JMP_IP
11651#else
11652				|	jmp ->trace_escape
11653#endif
11654			} else {
11655				|	CMP_IP next_opline
11656				|	jne ->trace_escape
11657			}
11658
11659			zend_jit_set_last_valid_opline(trace->opline);
11660
11661			return 1;
11662		} else if (may_throw ||
11663				(((opline->op1_type & (IS_VAR|IS_TMP_VAR))
11664				  && (op1_info & MAY_BE_RC1)
11665				  && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)))
11666				 && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) {
11667			|	// if (EG(exception))
11668			|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
11669			|	jne ->leave_throw_handler
11670		}
11671
11672		return 1;
11673	} else {
11674		|	// if (EG(exception))
11675		|	MEM_CMP_ZTS aword, executor_globals, exception, 0, r0
11676		|	LOAD_IP
11677		|	jne ->leave_throw_handler
11678		|	// opline = EX(opline) + 1
11679		|	ADD_IP sizeof(zend_op)
11680	}
11681
11682	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
11683		|	ADD_HYBRID_SPAD
11684#ifdef CONTEXT_THREADED_JIT
11685		|	push aword [IP]
11686		|	ret
11687#else
11688		|	JMP_IP
11689#endif
11690	} else if (GCC_GLOBAL_REGS) {
11691		|	add r4, SPAD // stack alignment
11692#ifdef CONTEXT_THREADED_JIT
11693		|	push aword [IP]
11694		|	ret
11695#else
11696		|	JMP_IP
11697#endif
11698	} else {
11699#ifdef CONTEXT_THREADED_JIT
11700		ZEND_UNREACHABLE();
11701		// TODO: context threading can't work without GLOBAL REGS because we have to change
11702		//       the value of execute_data in execute_ex()
11703		|	mov FCARG1a, FP
11704		|	mov r0, aword [FP]
11705		|	mov FP, aword T2 // restore FP
11706		|	mov RX, aword T3 // restore IP
11707		|	add r4, NR_SPAD // stack alignment
11708		|	push aword [r0]
11709		|	ret
11710#else
11711		|	mov FP, aword T2 // restore FP
11712		|	mov RX, aword T3 // restore IP
11713		|	add r4, NR_SPAD // stack alignment
11714		|	mov r0, 2 // ZEND_VM_LEAVE
11715		|	ret
11716#endif
11717	}
11718
11719	return 1;
11720}
11721
11722static 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)
11723{
11724	zend_jit_addr ret_addr;
11725	int8_t return_value_used;
11726
11727	ZEND_ASSERT(op_array->type != ZEND_EVAL_CODE && op_array->function_name);
11728	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF));
11729
11730	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && JIT_G(current_frame)) {
11731		if (TRACE_FRAME_IS_RETURN_VALUE_USED(JIT_G(current_frame))) {
11732			return_value_used = 1;
11733		} else if (TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))) {
11734			return_value_used = 0;
11735		} else {
11736			return_value_used = -1;
11737		}
11738	} else {
11739		return_value_used = -1;
11740	}
11741
11742	if (ZEND_OBSERVER_ENABLED) {
11743		if (Z_MODE(op1_addr) == IS_REG) {
11744			zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
11745
11746			if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) {
11747				return 0;
11748			}
11749			op1_addr = dst;
11750		}
11751		|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
11752		|	mov FCARG1a, FP
11753		|	SET_EX_OPLINE opline, r0
11754		|	EXT_CALL zend_observer_fcall_end, r0
11755	}
11756
11757	// if (!EX(return_value))
11758	if (Z_MODE(op1_addr) == IS_REG && Z_REG(op1_addr) == ZREG_R1) {
11759		if (return_value_used != 0) {
11760			|	mov r2, EX->return_value
11761		}
11762		if (return_value_used == -1) {
11763			|	test r2, r2
11764		}
11765		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R2, 0);
11766	} else {
11767		if (return_value_used != 0) {
11768			|	mov r1, EX->return_value
11769		}
11770		if (return_value_used == -1) {
11771			|	test r1, r1
11772		}
11773		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R1, 0);
11774	}
11775	if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
11776	    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11777		if (return_value_used == -1) {
11778			|	jz >1
11779			|.cold_code
11780			|1:
11781		}
11782		if (return_value_used != 1) {
11783			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11784				if (jit_return_label >= 0) {
11785					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, =>jit_return_label
11786				} else {
11787					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >9
11788				}
11789			}
11790			|	GET_ZVAL_PTR FCARG1a, op1_addr
11791			|	GC_DELREF FCARG1a
11792			if (RC_MAY_BE_1(op1_info)) {
11793				if (RC_MAY_BE_N(op1_info)) {
11794					if (jit_return_label >= 0) {
11795						|	jnz =>jit_return_label
11796					} else {
11797						|	jnz >9
11798					}
11799				}
11800				|	//SAVE_OPLINE()
11801				|	ZVAL_DTOR_FUNC op1_info, opline
11802				|	//????mov r1, EX->return_value // reload ???
11803			}
11804			if (return_value_used == -1) {
11805				if (jit_return_label >= 0) {
11806					|	jmp =>jit_return_label
11807				} else {
11808					|	jmp >9
11809				}
11810				|.code
11811			}
11812		}
11813	} else if (return_value_used == -1) {
11814		if (jit_return_label >= 0) {
11815			|	jz =>jit_return_label
11816		} else {
11817			|	jz >9
11818		}
11819	}
11820
11821	if (return_value_used == 0) {
11822		|9:
11823		return 1;
11824	}
11825
11826	if (opline->op1_type == IS_CONST) {
11827		zval *zv = RT_CONSTANT(opline, opline->op1);
11828		|	ZVAL_COPY_CONST ret_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_R0
11829		if (Z_REFCOUNTED_P(zv)) {
11830			|	ADDREF_CONST zv, r0
11831		}
11832	} else if (opline->op1_type == IS_TMP_VAR) {
11833		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
11834	} else if (opline->op1_type == IS_CV) {
11835		if (op1_info & MAY_BE_REF) {
11836			|	LOAD_ZVAL_ADDR r0, op1_addr
11837			|	ZVAL_DEREF r0, op1_info
11838			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
11839		}
11840		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
11841		if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
11842			if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11843			    (op1_info & (MAY_BE_REF|MAY_BE_OBJECT)) ||
11844			    !op_array->function_name) {
11845				|	TRY_ADDREF op1_info, ah, r2
11846			} else if (return_value_used != 1) {
11847				|	// if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) ZVAL_NULL(retval_ptr);
11848				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL
11849			}
11850		}
11851	} else {
11852		if (op1_info & MAY_BE_REF) {
11853			zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, offsetof(zend_reference, val));
11854
11855			|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1
11856			|.cold_code
11857			|1:
11858			|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
11859			|	GET_ZVAL_PTR r0, op1_addr
11860			|	// ZVAL_COPY_VALUE(return_value, &ref->value);
11861			|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_R2, ZREG_R2
11862			|	GC_DELREF r0
11863			|	je >2
11864			|	// if (IS_REFCOUNTED())
11865			if (jit_return_label >= 0) {
11866				|	IF_NOT_REFCOUNTED dh, =>jit_return_label
11867			} else {
11868				|	IF_NOT_REFCOUNTED dh, >9
11869			}
11870			|	// ADDREF
11871			|	GET_ZVAL_PTR r2, ret_addr // reload
11872			|	GC_ADDREF r2
11873			if (jit_return_label >= 0) {
11874				|	jmp =>jit_return_label
11875			} else {
11876				|	jmp >9
11877			}
11878			|2:
11879			|	EFREE_REFERENCE r0
11880			if (jit_return_label >= 0) {
11881				|	jmp =>jit_return_label
11882			} else {
11883				|	jmp >9
11884			}
11885			|.code
11886		}
11887		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2
11888	}
11889
11890	|9:
11891	return 1;
11892}
11893
11894static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, zend_jit_addr val_addr, zend_reg type_reg)
11895{
11896	ZEND_ASSERT(type_reg == ZREG_R2);
11897
11898	|.if not(X64)
11899	||	if (Z_REG(val_addr) == ZREG_R1) {
11900	|	GET_ZVAL_W2 r0, val_addr
11901	||	}
11902	|.endif
11903	|	GET_ZVAL_PTR r1, val_addr
11904	|.if not(X64)
11905	||	if (Z_REG(val_addr) != ZREG_R1) {
11906	|	GET_ZVAL_W2 r0, val_addr
11907	||	}
11908	|.endif
11909	|	IF_NOT_REFCOUNTED dh, >2
11910	|	IF_NOT_TYPE dl, IS_REFERENCE, >1
11911	|	GET_Z_TYPE_INFO edx, r1+offsetof(zend_reference, val)
11912	|.if not(X64)
11913	|	GET_Z_W2 r0, r1+offsetof(zend_reference, val)
11914	|.endif
11915	|	GET_Z_PTR r1, r1+offsetof(zend_reference, val)
11916	|	IF_NOT_REFCOUNTED dh, >2
11917	|1:
11918	|	GC_ADDREF r1
11919	|2:
11920	|	SET_ZVAL_PTR res_addr, r1
11921	|.if not(X64)
11922	|	SET_ZVAL_W2 res_addr, r0
11923	|.endif
11924	|	SET_ZVAL_TYPE_INFO res_addr, edx
11925
11926	return 1;
11927}
11928
11929static int zend_jit_fetch_dim_read(dasm_State        **Dst,
11930                                   const zend_op      *opline,
11931                                   zend_ssa           *ssa,
11932                                   const zend_ssa_op  *ssa_op,
11933                                   uint32_t            op1_info,
11934                                   zend_jit_addr       op1_addr,
11935                                   bool           op1_avoid_refcounting,
11936                                   uint32_t            op2_info,
11937                                   uint32_t            res_info,
11938                                   zend_jit_addr       res_addr,
11939                                   uint8_t             dim_type)
11940{
11941	zend_jit_addr orig_op1_addr, op2_addr;
11942	const void *exit_addr = NULL;
11943	const void *not_found_exit_addr = NULL;
11944	const void *res_exit_addr = NULL;
11945	bool result_avoid_refcounting = 0;
11946	uint32_t may_be_string = (opline->opcode != ZEND_FETCH_LIST_R) ? MAY_BE_STRING : 0;
11947	int may_throw = 0;
11948
11949	orig_op1_addr = OP1_ADDR();
11950	op2_addr = OP2_ADDR();
11951
11952	if (opline->opcode != ZEND_FETCH_DIM_IS
11953	 && JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
11954		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
11955		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
11956		if (!exit_addr) {
11957			return 0;
11958		}
11959	}
11960
11961	if ((res_info & MAY_BE_GUARD)
11962	 && JIT_G(current_frame)
11963	 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
11964		uint32_t flags = 0;
11965		uint32_t old_op1_info = 0;
11966		uint32_t old_info;
11967		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
11968		int32_t exit_point;
11969
11970		if (opline->opcode != ZEND_FETCH_LIST_R
11971		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR))
11972		 && !op1_avoid_refcounting) {
11973			flags |= ZEND_JIT_EXIT_FREE_OP1;
11974		}
11975		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
11976		 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11977			flags |= ZEND_JIT_EXIT_FREE_OP2;
11978		}
11979		if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
11980		 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
11981		 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
11982		 && (ssa_op+1)->op1_use == ssa_op->result_def
11983		 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))
11984		 && zend_jit_may_avoid_refcounting(opline+1, res_info)) {
11985			result_avoid_refcounting = 1;
11986			ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
11987		}
11988
11989		if (op1_avoid_refcounting) {
11990			old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
11991			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
11992		}
11993
11994		if (!(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))) {
11995			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
11996			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
11997			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
11998			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
11999			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
12000			res_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12001			if (!res_exit_addr) {
12002				return 0;
12003			}
12004			res_info &= ~MAY_BE_GUARD;
12005			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
12006		}
12007
12008		if (opline->opcode == ZEND_FETCH_DIM_IS
12009		 && !(res_info & MAY_BE_NULL)) {
12010			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
12011			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_NULL, 0);
12012			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_NULL);
12013			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
12014			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
12015			not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12016			if (!not_found_exit_addr) {
12017				return 0;
12018			}
12019		}
12020
12021		if (op1_avoid_refcounting) {
12022			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
12023		}
12024	}
12025
12026	if (op1_info & MAY_BE_REF) {
12027		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12028		|	ZVAL_DEREF FCARG1a, op1_info
12029		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12030	}
12031
12032	if (op1_info & MAY_BE_ARRAY) {
12033		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
12034			if (exit_addr && !(op1_info & (MAY_BE_OBJECT|may_be_string))) {
12035				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, &exit_addr
12036			} else {
12037				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
12038			}
12039		}
12040		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
12041		if ((op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) ||
12042		    (opline->opcode != ZEND_FETCH_DIM_IS && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE)) {
12043			may_throw = 1;
12044		}
12045		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)) {
12046			return 0;
12047		}
12048	}
12049
12050	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
12051		if (op1_info & MAY_BE_ARRAY) {
12052			|.cold_code
12053			|7:
12054		}
12055
12056		if (opline->opcode != ZEND_FETCH_LIST_R && (op1_info & MAY_BE_STRING)) {
12057			may_throw = 1;
12058			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING))) {
12059				if (exit_addr && !(op1_info & MAY_BE_OBJECT)) {
12060					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &exit_addr
12061				} else {
12062					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6
12063				}
12064			}
12065			|	SET_EX_OPLINE opline, r0
12066			|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
12067			if (opline->opcode != ZEND_FETCH_DIM_IS) {
12068				if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) {
12069					|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr
12070					|	EXT_CALL zend_jit_fetch_dim_str_offset_r_helper, r0
12071				} else {
12072					|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
12073					|	EXT_CALL zend_jit_fetch_dim_str_r_helper, r0
12074				}
12075				|	SET_ZVAL_PTR res_addr, r0
12076				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING
12077			} else {
12078				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
12079				|.if X64
12080					|   LOAD_ZVAL_ADDR CARG3, res_addr
12081				|.else
12082					|	sub r4, 12
12083					|   PUSH_ZVAL_ADDR res_addr, r0
12084				|.endif
12085				|	EXT_CALL zend_jit_fetch_dim_str_is_helper, r0
12086				|.if not(X64)
12087				|	add r4, 12
12088				|.endif
12089			}
12090			if ((op1_info & MAY_BE_ARRAY) ||
12091				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING)))) {
12092				|	jmp >9 // END
12093			}
12094			|6:
12095		}
12096
12097		if (op1_info & MAY_BE_OBJECT) {
12098			may_throw = 1;
12099			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string))) {
12100				if (exit_addr) {
12101					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
12102				} else {
12103					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >6
12104				}
12105			}
12106			|	SET_EX_OPLINE opline, r0
12107		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12108				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12109		    }
12110			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
12111				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
12112				|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
12113			} else {
12114				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
12115			}
12116			|.if X64
12117				|   LOAD_ZVAL_ADDR CARG3, res_addr
12118			|.else
12119				|	sub r4, 12
12120				|   PUSH_ZVAL_ADDR res_addr, r0
12121			|.endif
12122			if (opline->opcode != ZEND_FETCH_DIM_IS) {
12123				|	EXT_CALL zend_jit_fetch_dim_obj_r_helper, r0
12124			} else {
12125				|	EXT_CALL zend_jit_fetch_dim_obj_is_helper, r0
12126			}
12127			|.if not(X64)
12128			|	add r4, 12
12129			|.endif
12130			if ((op1_info & MAY_BE_ARRAY) ||
12131				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) {
12132				|	jmp >9 // END
12133			}
12134			|6:
12135		}
12136
12137		if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))
12138		 && (!exit_addr || !(op1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) {
12139			if ((opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) || (op2_info & MAY_BE_UNDEF)) {
12140				|	SET_EX_OPLINE opline, r0
12141				if (opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) {
12142					may_throw = 1;
12143					|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
12144					|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
12145					|	mov FCARG1d, opline->op1.var
12146					|	EXT_CALL zend_jit_undefined_op_helper, r0
12147					|1:
12148				}
12149
12150				if (op2_info & MAY_BE_UNDEF) {
12151					may_throw = 1;
12152					|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1
12153					|	mov FCARG1d, opline->op2.var
12154					|	EXT_CALL zend_jit_undefined_op_helper, r0
12155					|1:
12156				}
12157			}
12158
12159			if (opline->opcode != ZEND_FETCH_DIM_IS && opline->opcode != ZEND_FETCH_LIST_R) {
12160				may_throw = 1;
12161				if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
12162					|	LOAD_ZVAL_ADDR FCARG1a, orig_op1_addr
12163				} else {
12164					|	SET_EX_OPLINE opline, r0
12165					if (Z_MODE(op1_addr) != IS_MEM_ZVAL ||
12166					    Z_REG(op1_addr) != ZREG_FCARG1 ||
12167					    Z_OFFSET(op1_addr) != 0) {
12168						|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12169					}
12170				}
12171				|	EXT_CALL zend_jit_invalid_array_access, r0
12172			}
12173			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
12174			if (op1_info & MAY_BE_ARRAY) {
12175				|	jmp >9 // END
12176			}
12177		}
12178
12179		if (op1_info & MAY_BE_ARRAY) {
12180			|.code
12181		}
12182	}
12183
12184	if (op1_info & MAY_BE_ARRAY) {
12185		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
12186
12187		|8:
12188		if (res_exit_addr) {
12189			zend_uchar type = concrete_type(res_info);
12190
12191			if ((op1_info & MAY_BE_ARRAY_OF_REF)
12192			 && dim_type != IS_UNKNOWN
12193			 && dim_type != IS_REFERENCE) {
12194				if (type < IS_STRING) {
12195					|	IF_NOT_ZVAL_TYPE val_addr, type, >1
12196					|.cold_code
12197					|1:
12198					|	IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, &res_exit_addr
12199					|	GET_Z_PTR r0, r0
12200					|	add r0, offsetof(zend_reference, val)
12201					|	IF_ZVAL_TYPE val_addr, type, >1
12202					|	jmp &res_exit_addr
12203					|.code
12204					|1:
12205				} else {
12206					|	GET_ZVAL_TYPE_INFO edx, val_addr
12207					|	IF_NOT_TYPE dl, type, >1
12208					|.cold_code
12209					|1:
12210					|	IF_NOT_TYPE dl, IS_REFERENCE, &res_exit_addr
12211					|	GET_Z_PTR r0, r0
12212					|	add r0, offsetof(zend_reference, val)
12213					|	GET_ZVAL_TYPE_INFO edx, val_addr
12214					|	IF_TYPE dl, type, >1
12215					|	jmp &res_exit_addr
12216					|.code
12217					|1:
12218				}
12219			} else {
12220				if (op1_info & MAY_BE_ARRAY_OF_REF) {
12221					|	ZVAL_DEREF r0, MAY_BE_REF
12222				}
12223				if (type < IS_STRING) {
12224					|	IF_NOT_ZVAL_TYPE val_addr, type, &res_exit_addr
12225				} else {
12226					|	GET_ZVAL_TYPE_INFO edx, val_addr
12227					|	IF_NOT_TYPE dl, type, &res_exit_addr
12228				}
12229			}
12230
12231			|	// ZVAL_COPY
12232			|7:
12233			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_R0, ZREG_R1
12234			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
12235				if (type < IS_STRING) {
12236					if (Z_REG(res_addr) != ZREG_FP ||
12237					    JIT_G(current_frame) == NULL ||
12238					    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
12239						|	SET_ZVAL_TYPE_INFO res_addr, type
12240					}
12241				} else {
12242					|	SET_ZVAL_TYPE_INFO res_addr, edx
12243					if (!result_avoid_refcounting) {
12244						|	TRY_ADDREF res_info, dh, r1
12245					}
12246				}
12247			} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
12248				return 0;
12249			}
12250		} else if (op1_info & MAY_BE_ARRAY_OF_REF) {
12251			|	// ZVAL_COPY_DEREF
12252			|	GET_ZVAL_TYPE_INFO Rd(ZREG_R2), val_addr
12253			if (!zend_jit_zval_copy_deref(Dst, res_addr, val_addr, ZREG_R2)) {
12254				return 0;
12255			}
12256		} else  {
12257			|	// ZVAL_COPY
12258			|	ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_R1, ZREG_R2
12259			|	TRY_ADDREF res_info, ch, r2
12260		}
12261	}
12262	|9: // END
12263
12264#ifdef ZEND_JIT_USE_RC_INFERENCE
12265	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
12266		/* Magic offsetGet() may increase refcount of the key */
12267		op2_info |= MAY_BE_RCN;
12268	}
12269#endif
12270
12271    if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
12272		if ((op2_info & MAY_HAVE_DTOR) && (op2_info & MAY_BE_RC1)) {
12273			may_throw = 1;
12274		}
12275		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12276	}
12277	if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting) {
12278		if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
12279			if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
12280				may_throw = 1;
12281			}
12282			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
12283		}
12284	}
12285
12286	if (may_throw) {
12287		if (!zend_jit_check_exception(Dst)) {
12288			return 0;
12289		}
12290	}
12291
12292	return 1;
12293}
12294
12295static int zend_jit_fetch_dim(dasm_State    **Dst,
12296                              const zend_op  *opline,
12297                              uint32_t        op1_info,
12298                              zend_jit_addr   op1_addr,
12299                              uint32_t        op2_info,
12300                              zend_jit_addr   res_addr,
12301                              uint8_t         dim_type)
12302{
12303	zend_jit_addr op2_addr;
12304	int may_throw = 0;
12305
12306	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
12307
12308	if (opline->opcode == ZEND_FETCH_DIM_RW) {
12309		|	SET_EX_OPLINE opline, r0
12310	}
12311	if (op1_info & MAY_BE_REF) {
12312		may_throw = 1;
12313		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12314		|	IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
12315		|	GET_Z_PTR FCARG2a, FCARG1a
12316		|	IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
12317		|	lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
12318		|	jmp >3
12319		|.cold_code
12320		|2:
12321		if (opline->opcode != ZEND_FETCH_DIM_RW) {
12322			|	SET_EX_OPLINE opline, r0
12323		}
12324		|	EXT_CALL zend_jit_prepare_assign_dim_ref, r0
12325		|	test r0, r0
12326		|	mov FCARG1a, r0
12327		|	jne >1
12328		|	jmp ->exception_handler_undef
12329		|.code
12330		|1:
12331		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12332	}
12333
12334	if (op1_info & MAY_BE_ARRAY) {
12335		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
12336			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
12337		}
12338		|3:
12339		|	SEPARATE_ARRAY op1_addr, op1_info, 1
12340	}
12341	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
12342		if (op1_info & MAY_BE_ARRAY) {
12343			|.cold_code
12344			|7:
12345		}
12346		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
12347			|	CMP_ZVAL_TYPE op1_addr, IS_NULL
12348			|	jg >7
12349		}
12350		if (Z_REG(op1_addr) != ZREG_FP) {
12351			|	mov T1, Ra(Z_REG(op1_addr)) // save
12352		}
12353		if ((op1_info & MAY_BE_UNDEF)
12354		 && opline->opcode == ZEND_FETCH_DIM_RW) {
12355			may_throw = 1;
12356			if (op1_info & MAY_BE_NULL) {
12357				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
12358			}
12359			|	mov FCARG1a, opline->op1.var
12360			|	EXT_CALL zend_jit_undefined_op_helper, r0
12361			|1:
12362		}
12363		|	// ZVAL_ARR(container, zend_new_array(8));
12364		|	EXT_CALL _zend_new_array_0, r0
12365		if (Z_REG(op1_addr) != ZREG_FP) {
12366			|	mov Ra(Z_REG(op1_addr)), T1 // restore
12367		}
12368		|	SET_ZVAL_LVAL op1_addr, r0
12369		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
12370		|	mov FCARG1a, r0
12371		if (op1_info & MAY_BE_ARRAY) {
12372			|	jmp >1
12373			|.code
12374			|1:
12375		}
12376	}
12377
12378	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
12379		|6:
12380		if (opline->op2_type == IS_UNUSED) {
12381			may_throw = 1;
12382			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
12383			|	LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
12384			|	EXT_CALL zend_hash_next_index_insert, r0
12385			|	// if (UNEXPECTED(!var_ptr)) {
12386			|	test r0, r0
12387			|	jz >1
12388			|.cold_code
12389			|1:
12390			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
12391			|	CANNOT_ADD_ELEMENT opline
12392			|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF
12393			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
12394			|	jmp >8
12395			|.code
12396			|	SET_ZVAL_PTR res_addr, r0
12397			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
12398		} else {
12399			uint32_t type;
12400
12401			switch (opline->opcode) {
12402				case ZEND_FETCH_DIM_W:
12403				case ZEND_FETCH_LIST_W:
12404					type = BP_VAR_W;
12405					break;
12406				case ZEND_FETCH_DIM_RW:
12407					may_throw = 1;
12408					type = BP_VAR_RW;
12409					break;
12410				case ZEND_FETCH_DIM_UNSET:
12411					type = BP_VAR_UNSET;
12412					break;
12413				default:
12414					ZEND_UNREACHABLE();
12415			}
12416
12417			if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
12418				may_throw = 1;
12419			}
12420			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, type, op1_info, op2_info, dim_type, NULL, NULL, NULL)) {
12421				return 0;
12422			}
12423
12424			|8:
12425			|	SET_ZVAL_PTR res_addr, r0
12426			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
12427
12428			if (type == BP_VAR_RW || (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
12429				|.cold_code
12430				|9:
12431				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
12432				|	jmp >8
12433				|.code
12434			}
12435		}
12436	}
12437
12438	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
12439		may_throw = 1;
12440		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
12441			|.cold_code
12442			|7:
12443		}
12444
12445		if (opline->opcode != ZEND_FETCH_DIM_RW) {
12446			|	SET_EX_OPLINE opline, r0
12447		}
12448		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12449			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12450		}
12451	    if (opline->op2_type == IS_UNUSED) {
12452			|	xor FCARG2a, FCARG2a
12453		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
12454			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
12455			|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
12456		} else {
12457			|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
12458		}
12459		|.if X64
12460			|	LOAD_ZVAL_ADDR CARG3, res_addr
12461		|.else
12462			|	sub r4, 12
12463			|	PUSH_ZVAL_ADDR res_addr, r0
12464		|.endif
12465		switch (opline->opcode) {
12466			case ZEND_FETCH_DIM_W:
12467			case ZEND_FETCH_LIST_W:
12468				|	EXT_CALL zend_jit_fetch_dim_obj_w_helper, r0
12469				break;
12470			case ZEND_FETCH_DIM_RW:
12471				|	EXT_CALL zend_jit_fetch_dim_obj_rw_helper, r0
12472				break;
12473//			case ZEND_FETCH_DIM_UNSET:
12474//				|	EXT_CALL zend_jit_fetch_dim_obj_unset_helper, r0
12475//				break;
12476			default:
12477				ZEND_UNREACHABLE();
12478			}
12479		|.if not(X64)
12480		|	add r4, 12
12481		|.endif
12482
12483		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
12484			|	jmp >8 // END
12485			|.code
12486		}
12487	}
12488
12489#ifdef ZEND_JIT_USE_RC_INFERENCE
12490	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))) {
12491		/* ASSIGN_DIM may increase refcount of the key */
12492		op2_info |= MAY_BE_RCN;
12493	}
12494#endif
12495
12496	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
12497	 && (op2_info & MAY_HAVE_DTOR)
12498	 && (op2_info & MAY_BE_RC1)) {
12499		may_throw = 1;
12500	}
12501	|8:
12502	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12503
12504	if (may_throw) {
12505		if (!zend_jit_check_exception(Dst)) {
12506			return 0;
12507		}
12508	}
12509
12510	return 1;
12511}
12512
12513static int zend_jit_isset_isempty_dim(dasm_State    **Dst,
12514                                      const zend_op  *opline,
12515                                      uint32_t        op1_info,
12516                                      zend_jit_addr   op1_addr,
12517                                      bool       op1_avoid_refcounting,
12518                                      uint32_t        op2_info,
12519                                      uint8_t         dim_type,
12520                                      int             may_throw,
12521                                      zend_uchar      smart_branch_opcode,
12522                                      uint32_t        target_label,
12523                                      uint32_t        target_label2,
12524                                      const void     *exit_addr)
12525{
12526	zend_jit_addr op2_addr, res_addr;
12527
12528	// TODO: support for empty() ???
12529	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
12530
12531	op2_addr = OP2_ADDR();
12532	res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12533
12534	if (op1_info & MAY_BE_REF) {
12535		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12536		|	ZVAL_DEREF FCARG1a, op1_info
12537		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12538	}
12539
12540	if (op1_info & MAY_BE_ARRAY) {
12541		const void *found_exit_addr = NULL;
12542		const void *not_found_exit_addr = NULL;
12543
12544		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
12545			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
12546		}
12547		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr
12548		if (exit_addr
12549		 && !(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY))
12550		 && !may_throw
12551		 && (!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || op1_avoid_refcounting)
12552		 && (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)))) {
12553			if (smart_branch_opcode == ZEND_JMPNZ) {
12554				found_exit_addr = exit_addr;
12555			} else {
12556				not_found_exit_addr = exit_addr;
12557			}
12558		}
12559		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)) {
12560			return 0;
12561		}
12562
12563		if (found_exit_addr) {
12564			|9:
12565			return 1;
12566		} else if (not_found_exit_addr) {
12567			|8:
12568			return 1;
12569		}
12570	}
12571
12572	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
12573		if (op1_info & MAY_BE_ARRAY) {
12574			|.cold_code
12575			|7:
12576		}
12577
12578		if (op1_info & (MAY_BE_STRING|MAY_BE_OBJECT)) {
12579			|	SET_EX_OPLINE opline, r0
12580		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12581				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
12582			}
12583			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
12584				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
12585				|	LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
12586			} else {
12587				|	LOAD_ZVAL_ADDR FCARG2a, op2_addr
12588			}
12589			|	EXT_CALL zend_jit_isset_dim_helper, r0
12590			|	test r0, r0
12591			|	jz >9
12592			if (op1_info & MAY_BE_ARRAY) {
12593				|	jmp >8
12594				|.code
12595			}
12596		} else {
12597			if (op2_info & MAY_BE_UNDEF) {
12598				if (op2_info & MAY_BE_ANY) {
12599					|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1
12600				}
12601				|	SET_EX_OPLINE opline, r0
12602				|	mov FCARG1d, opline->op2.var
12603				|	EXT_CALL zend_jit_undefined_op_helper, r0
12604				|1:
12605			}
12606			if (op1_info & MAY_BE_ARRAY) {
12607				|	jmp >9
12608				|.code
12609			}
12610		}
12611	}
12612
12613#ifdef ZEND_JIT_USE_RC_INFERENCE
12614	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
12615		/* Magic offsetExists() may increase refcount of the key */
12616		op2_info |= MAY_BE_RCN;
12617	}
12618#endif
12619
12620	if (op1_info & (MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_OBJECT)) {
12621		|8:
12622		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12623		if (!op1_avoid_refcounting) {
12624			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
12625		}
12626		if (may_throw) {
12627			if (!zend_jit_check_exception_undef_result(Dst, opline)) {
12628				return 0;
12629			}
12630		}
12631		if (!(opline->extended_value & ZEND_ISEMPTY)) {
12632			if (exit_addr) {
12633				if (smart_branch_opcode == ZEND_JMPNZ) {
12634					|	jmp &exit_addr
12635				} else {
12636					|	jmp >8
12637				}
12638			} else if (smart_branch_opcode) {
12639				if (smart_branch_opcode == ZEND_JMPZ) {
12640					|	jmp =>target_label2
12641				} else if (smart_branch_opcode == ZEND_JMPNZ) {
12642					|	jmp =>target_label
12643				} else if (smart_branch_opcode == ZEND_JMPZNZ) {
12644					|	jmp =>target_label2
12645				} else {
12646					ZEND_UNREACHABLE();
12647				}
12648			} else {
12649				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
12650				|	jmp >8
12651			}
12652		} else {
12653			|	NIY // TODO: support for empty()
12654		}
12655	}
12656
12657	|9: // not found
12658	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
12659	if (!op1_avoid_refcounting) {
12660		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
12661	}
12662	if (may_throw) {
12663		if (!zend_jit_check_exception_undef_result(Dst, opline)) {
12664			return 0;
12665		}
12666	}
12667	if (!(opline->extended_value & ZEND_ISEMPTY)) {
12668		if (exit_addr) {
12669			if (smart_branch_opcode == ZEND_JMPZ) {
12670				|	jmp &exit_addr
12671			}
12672		} else if (smart_branch_opcode) {
12673			if (smart_branch_opcode == ZEND_JMPZ) {
12674				|	jmp =>target_label
12675			} else if (smart_branch_opcode == ZEND_JMPNZ) {
12676			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
12677				|	jmp =>target_label
12678			} else {
12679				ZEND_UNREACHABLE();
12680			}
12681		} else {
12682			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
12683		}
12684	} else {
12685		|	NIY // TODO: support for empty()
12686	}
12687
12688	|8:
12689
12690	return 1;
12691}
12692
12693static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
12694{
12695	zend_jit_addr op1_addr = OP1_ADDR();
12696	zend_string *varname = Z_STR_P(RT_CONSTANT(opline, opline->op2));
12697
12698	|	// idx = (uint32_t)(uintptr_t)CACHED_PTR(opline->extended_value) - 1;
12699	|	mov FCARG2a, EX->run_time_cache
12700	|	mov r0, aword [FCARG2a + opline->extended_value]
12701	|	sub r0, 1
12702	|	// if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket)))
12703	|	MEM_LOAD_ZTS ecx, dword, executor_globals, symbol_table.nNumUsed, r1
12704	|.if X64
12705		|	shl r1, 5
12706	|.else
12707		|	imul r1, sizeof(Bucket)
12708	|.endif
12709	|	cmp r0, r1
12710	|	jae >9
12711	|	// Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx);
12712	|	MEM_LOAD_OP_ZTS add, r0, aword, executor_globals, symbol_table.arData, r1
12713	|	IF_NOT_Z_TYPE r0, IS_REFERENCE, >9
12714	|	// (EXPECTED(p->key == varname))
12715	|	ADDR_CMP aword [r0 + offsetof(Bucket, key)], varname, r1
12716	|	jne >9
12717	|	GET_Z_PTR r0, r0
12718	|	GC_ADDREF r0
12719	|1:
12720	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
12721		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
12722			|	// if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr)))
12723			|	IF_ZVAL_REFCOUNTED op1_addr, >2
12724			|.cold_code
12725			|2:
12726		}
12727		|	// zend_refcounted *garbage = Z_COUNTED_P(variable_ptr);
12728		|	GET_ZVAL_PTR FCARG1a, op1_addr
12729		|	// ZVAL_REF(variable_ptr, ref)
12730		|	SET_ZVAL_PTR op1_addr, r0
12731		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX
12732		|	// if (GC_DELREF(garbage) == 0)
12733		|	GC_DELREF FCARG1a
12734		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
12735			|	jnz >3
12736		} else {
12737			|	jnz >5
12738		}
12739		|	ZVAL_DTOR_FUNC op1_info, opline
12740		|	jmp >5
12741		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
12742			|3:
12743			|	// GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr)
12744			|	IF_GC_MAY_NOT_LEAK FCARG1a, >5
12745			|	EXT_CALL gc_possible_root, r1
12746			|	jmp >5
12747		}
12748		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
12749			|.code
12750		}
12751	}
12752
12753	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
12754		|	// ZVAL_REF(variable_ptr, ref)
12755		|	SET_ZVAL_PTR op1_addr, r0
12756		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX
12757	}
12758	|5:
12759	//END of handler
12760
12761	|.cold_code
12762	|9:
12763	|	LOAD_ADDR FCARG1a, (ptrdiff_t)varname
12764	if (opline->extended_value) {
12765		|	add FCARG2a, opline->extended_value
12766	}
12767	|	EXT_CALL zend_jit_fetch_global_helper, r0
12768	|	jmp <1
12769	|.code
12770
12771	return 1;
12772}
12773
12774static int zend_jit_verify_arg_type(dasm_State **Dst, const zend_op *opline, zend_arg_info *arg_info, bool check_exception)
12775{
12776	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12777	bool in_cold = 0;
12778	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
12779	zend_reg tmp_reg = (type_mask == 0 || is_power_of_two(type_mask)) ? ZREG_FCARG1 : ZREG_R0;
12780
12781	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
12782	 && JIT_G(current_frame)
12783	 && JIT_G(current_frame)->prev) {
12784		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
12785		uint8_t type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var));
12786
12787		if (type != IS_UNKNOWN && (type_mask & (1u << type))) {
12788			return 1;
12789		}
12790	}
12791
12792	if (ZEND_ARG_SEND_MODE(arg_info)) {
12793		if (opline->opcode == ZEND_RECV_INIT) {
12794			|	LOAD_ZVAL_ADDR Ra(tmp_reg), res_addr
12795			|	ZVAL_DEREF Ra(tmp_reg), MAY_BE_REF
12796			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, 0);
12797		} else {
12798			|	GET_ZVAL_PTR Ra(tmp_reg), res_addr
12799			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, offsetof(zend_reference, val));
12800		}
12801	}
12802
12803	if (type_mask != 0) {
12804		if (is_power_of_two(type_mask)) {
12805			uint32_t type_code = concrete_type(type_mask);
12806			|	IF_NOT_ZVAL_TYPE res_addr, type_code, >1
12807		} else {
12808			|	mov edx, 1
12809			|	mov cl, byte [Ra(Z_REG(res_addr))+Z_OFFSET(res_addr)+offsetof(zval, u1.v.type)]
12810			|	shl edx, cl
12811			|	test edx, type_mask
12812			|	je >1
12813		}
12814
12815		|.cold_code
12816		|1:
12817
12818		in_cold = 1;
12819	}
12820
12821	if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
12822		|	LOAD_ZVAL_ADDR FCARG1a, res_addr
12823	}
12824	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12825		|	SET_EX_OPLINE opline, r0
12826	} else {
12827		|	ADDR_STORE aword EX->opline, opline, r0
12828	}
12829	|	LOAD_ADDR FCARG2a, (ptrdiff_t)arg_info
12830	|	EXT_CALL zend_jit_verify_arg_slow, r0
12831
12832	if (check_exception) {
12833		|	test al, al
12834		if (in_cold) {
12835			|	jnz >1
12836			|	jmp ->exception_handler
12837			|.code
12838			|1:
12839		} else {
12840			|	jz ->exception_handler
12841		}
12842	} else if (in_cold) {
12843		|	jmp >1
12844		|.code
12845		|1:
12846	}
12847
12848	return 1;
12849}
12850
12851static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array)
12852{
12853	uint32_t arg_num = opline->op1.num;
12854	zend_arg_info *arg_info = NULL;
12855
12856	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
12857		if (EXPECTED(arg_num <= op_array->num_args)) {
12858			arg_info = &op_array->arg_info[arg_num-1];
12859		} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
12860			arg_info = &op_array->arg_info[op_array->num_args];
12861		}
12862		if (arg_info && !ZEND_TYPE_IS_SET(arg_info->type)) {
12863			arg_info = NULL;
12864		}
12865	}
12866
12867	if (arg_info || (opline+1)->opcode != ZEND_RECV) {
12868		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12869			if (!JIT_G(current_frame) ||
12870			    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) < 0 ||
12871			    arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) {
12872				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12873				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12874
12875				if (!exit_addr) {
12876					return 0;
12877				}
12878				|	cmp dword EX->This.u2.num_args, arg_num
12879				|	jb &exit_addr
12880			}
12881		} else {
12882			|	cmp dword EX->This.u2.num_args, arg_num
12883			|	jb >1
12884			|.cold_code
12885			|1:
12886			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12887				|	SET_EX_OPLINE opline, r0
12888			} else {
12889				|	ADDR_STORE aword EX->opline, opline, r0
12890			}
12891			|	mov FCARG1a, FP
12892			|	EXT_CALL zend_missing_arg_error, r0
12893			|	jmp ->exception_handler
12894			|.code
12895		}
12896	}
12897
12898	if (arg_info) {
12899		if (!zend_jit_verify_arg_type(Dst, opline, arg_info, 1)) {
12900			return 0;
12901		}
12902	}
12903
12904	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
12905		if ((opline+1)->opcode != ZEND_RECV && (opline+1)->opcode != ZEND_RECV_INIT) {
12906			|	LOAD_IP_ADDR (opline + 1)
12907			zend_jit_set_last_valid_opline(opline + 1);
12908		}
12909	}
12910
12911	return 1;
12912}
12913
12914static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool is_last, int may_throw)
12915{
12916	uint32_t arg_num = opline->op1.num;
12917	zval *zv = RT_CONSTANT(opline, opline->op2);
12918	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12919
12920	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
12921	 && JIT_G(current_frame)
12922	 && TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) >= 0) {
12923		if (arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) {
12924			|	ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_R0
12925			if (Z_REFCOUNTED_P(zv)) {
12926				|	ADDREF_CONST zv, r0
12927			}
12928		}
12929	} else {
12930		if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
12931		    (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
12932			|	cmp dword EX->This.u2.num_args, arg_num
12933			|	jae >5
12934		}
12935		|	ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_R0
12936		if (Z_REFCOUNTED_P(zv)) {
12937			|	ADDREF_CONST zv, r0
12938		}
12939	}
12940
12941	if (Z_CONSTANT_P(zv)) {
12942		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12943			|	SET_EX_OPLINE opline, r0
12944		} else {
12945			|	ADDR_STORE aword EX->opline, opline, r0
12946		}
12947		|	LOAD_ZVAL_ADDR FCARG1a, res_addr
12948		|	mov r0, EX->func
12949		|	mov FCARG2a, [r0 + offsetof(zend_op_array, scope)]
12950		|	.if X64
12951		|		EXT_CALL zval_update_constant_ex, r0
12952		|	.else
12953		||#if (PHP_VERSION_ID < 80100) && (SIZEOF_SIZE_T == 4)
12954		|		EXT_CALL zval_jit_update_constant_ex, r0
12955		||#else
12956		|		EXT_CALL zval_update_constant_ex, r0
12957		||#endif
12958		|	.endif
12959		|	test al, al
12960		|	jnz >1
12961		|.cold_code
12962		|1:
12963		|	ZVAL_PTR_DTOR res_addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 1, 0, opline
12964		|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF
12965		|	jmp ->exception_handler
12966		|.code
12967	}
12968
12969	|5:
12970
12971	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
12972		do {
12973			zend_arg_info *arg_info;
12974
12975			if (arg_num <= op_array->num_args) {
12976				arg_info = &op_array->arg_info[arg_num-1];
12977			} else if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
12978				arg_info = &op_array->arg_info[op_array->num_args];
12979			} else {
12980				break;
12981			}
12982			if (!ZEND_TYPE_IS_SET(arg_info->type)) {
12983				break;
12984			}
12985			if (!zend_jit_verify_arg_type(Dst, opline, arg_info, may_throw)) {
12986				return 0;
12987			}
12988		} while (0);
12989	}
12990
12991	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
12992		if (is_last) {
12993			|	LOAD_IP_ADDR (opline + 1)
12994			zend_jit_set_last_valid_opline(opline + 1);
12995		}
12996	}
12997
12998	return 1;
12999}
13000
13001static int zend_jit_class_guard(dasm_State **Dst, const zend_op *opline, zend_class_entry *ce)
13002{
13003	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
13004	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13005
13006	if (!exit_addr) {
13007		return 0;
13008	}
13009
13010	|.if X64
13011	||	if (!IS_SIGNED_32BIT(ce)) {
13012	|		mov64 r0, ((ptrdiff_t)ce)
13013	|		cmp aword [FCARG1a + offsetof(zend_object, ce)], r0
13014	||	} else {
13015	|		cmp aword [FCARG1a + offsetof(zend_object, ce)], ce
13016	||	}
13017	|.else
13018	|	cmp aword [FCARG1a + offsetof(zend_object, ce)], ce
13019	|.endif
13020	|	jne &exit_addr
13021
13022	return 1;
13023}
13024
13025static int zend_jit_fetch_obj(dasm_State          **Dst,
13026                              const zend_op        *opline,
13027                              const zend_op_array  *op_array,
13028                              zend_ssa             *ssa,
13029                              const zend_ssa_op    *ssa_op,
13030                              uint32_t              op1_info,
13031                              zend_jit_addr         op1_addr,
13032                              bool                  op1_indirect,
13033                              zend_class_entry     *ce,
13034                              bool                  ce_is_instanceof,
13035                              bool                  on_this,
13036                              bool                  delayed_fetch_this,
13037                              bool                  op1_avoid_refcounting,
13038                              zend_class_entry     *trace_ce,
13039                              uint8_t               prop_type,
13040                              int                   may_throw)
13041{
13042	zval *member;
13043	zend_property_info *prop_info;
13044	bool may_be_dynamic = 1;
13045	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
13046	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
13047	zend_jit_addr prop_addr;
13048	uint32_t res_info = RES_INFO();
13049	bool type_loaded = 0;
13050
13051	ZEND_ASSERT(opline->op2_type == IS_CONST);
13052	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
13053
13054	member = RT_CONSTANT(opline, opline->op2);
13055	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
13056	prop_info = zend_get_known_property_info(op_array, ce, Z_STR_P(member), on_this, op_array->filename);
13057
13058	if (on_this) {
13059		|	GET_ZVAL_PTR FCARG1a, this_addr
13060	} else {
13061		if (opline->op1_type == IS_VAR
13062		 && opline->opcode == ZEND_FETCH_OBJ_W
13063		 && (op1_info & MAY_BE_INDIRECT)
13064		 && Z_REG(op1_addr) == ZREG_FP) {
13065			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13066			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
13067			|	GET_Z_PTR FCARG1a, FCARG1a
13068			|1:
13069			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13070		}
13071		if (op1_info & MAY_BE_REF) {
13072			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13073				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13074			}
13075			|	ZVAL_DEREF FCARG1a, op1_info
13076			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13077		}
13078		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
13079			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13080				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13081				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13082
13083				if (!exit_addr) {
13084					return 0;
13085				}
13086				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
13087			} else {
13088				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >7
13089			}
13090		}
13091		|	GET_ZVAL_PTR FCARG1a, op1_addr
13092	}
13093
13094	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
13095		prop_info = zend_get_known_property_info(op_array, trace_ce, Z_STR_P(member), on_this, op_array->filename);
13096		if (prop_info) {
13097			ce = trace_ce;
13098			ce_is_instanceof = 0;
13099			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
13100				if (on_this && JIT_G(current_frame)
13101				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
13102					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
13103				} else if (zend_jit_class_guard(Dst, opline, ce)) {
13104					if (on_this && JIT_G(current_frame)) {
13105						JIT_G(current_frame)->ce = ce;
13106						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
13107					}
13108				} else {
13109					return 0;
13110				}
13111				if (ssa->var_info && ssa_op->op1_use >= 0) {
13112					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
13113					ssa->var_info[ssa_op->op1_use].ce = ce;
13114					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
13115				}
13116			}
13117		}
13118	}
13119
13120	if (!prop_info) {
13121		|	mov r0, EX->run_time_cache
13122		|	mov r2, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS)]
13123		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
13124		|	jne >5
13125		|	mov r0, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*)]
13126		may_be_dynamic = zend_may_be_dynamic_property(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
13127		if (may_be_dynamic) {
13128			|	test r0, r0
13129			if (opline->opcode == ZEND_FETCH_OBJ_W) {
13130				|	jl >5
13131			} else {
13132				|	jl >8 // dynamic property
13133			}
13134		}
13135		|	mov edx, dword [FCARG1a + r0 + 8]
13136		|	IF_UNDEF dl, >5
13137		|	add FCARG1a, r0
13138		type_loaded = 1;
13139		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13140		if (opline->opcode == ZEND_FETCH_OBJ_W
13141		 && (!ce ||	ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT)))) {
13142			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
13143
13144			|	mov r0, EX->run_time_cache
13145			|	mov FCARG2a, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2]
13146			|	test FCARG2a, FCARG2a
13147			|	jnz >1
13148			|.cold_code
13149			|1:
13150			|	test dword [FCARG2a + offsetof(zend_property_info, flags)], ZEND_ACC_READONLY
13151			if (flags) {
13152				|	jz >3
13153			} else {
13154				|	jz >4
13155			}
13156			|	IF_NOT_Z_TYPE FCARG1a, IS_OBJECT, >2
13157			|	GET_Z_PTR r0, FCARG1a
13158			|	GC_ADDREF r0
13159			|	SET_ZVAL_PTR res_addr, r0
13160			|	SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX
13161			|	jmp >9
13162			|2:
13163			|	mov FCARG1a, FCARG2a
13164			|	SET_EX_OPLINE opline, r0
13165			|	EXT_CALL zend_readonly_property_modification_error, r0
13166			|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR
13167			|	jmp >9
13168			|3:
13169			if (flags == ZEND_FETCH_DIM_WRITE) {
13170				|	SET_EX_OPLINE opline, r0
13171				|	EXT_CALL zend_jit_check_array_promotion, r0
13172				|	jmp >9
13173			} else if (flags == ZEND_FETCH_REF) {
13174				|.if X64
13175					|	LOAD_ZVAL_ADDR CARG3, res_addr
13176				|.else
13177					|	sub r4, 12
13178					|	PUSH_ZVAL_ADDR res_addr, r0
13179				|.endif
13180				|	EXT_CALL zend_jit_create_typed_ref, r0
13181				|.if not(X64)
13182				|	add r4, 12
13183				|.endif
13184				|	jmp >9
13185			} else {
13186				ZEND_ASSERT(flags == 0);
13187			}
13188			|.code
13189			|4:
13190		}
13191	} else {
13192		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
13193		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13194			if (opline->opcode == ZEND_FETCH_OBJ_W || !(res_info & MAY_BE_GUARD) || !JIT_G(current_frame)) {
13195				/* perform IS_UNDEF check only after result type guard (during deoptimization) */
13196				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13197				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13198
13199				if (!exit_addr) {
13200					return 0;
13201				}
13202				type_loaded = 1;
13203				|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13204				|	IF_UNDEF dl, &exit_addr
13205			}
13206		} else {
13207			type_loaded = 1;
13208			|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13209			|	IF_UNDEF dl, >5
13210		}
13211		if (opline->opcode == ZEND_FETCH_OBJ_W && (prop_info->flags & ZEND_ACC_READONLY)) {
13212			if (!type_loaded) {
13213				type_loaded = 1;
13214				|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13215			}
13216			|	IF_NOT_TYPE dl, IS_OBJECT, >4
13217			|	GET_ZVAL_PTR r0, prop_addr
13218			|	GC_ADDREF r0
13219			|	SET_ZVAL_PTR res_addr, r0
13220			|	SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX
13221			|	jmp >9
13222			|.cold_code
13223			|4:
13224			|	LOAD_ADDR FCARG1a, prop_info
13225			|	SET_EX_OPLINE opline, r0
13226			|	EXT_CALL zend_readonly_property_modification_error, r0
13227			|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR
13228			|	jmp >9
13229			|.code
13230		}
13231		if (opline->opcode == ZEND_FETCH_OBJ_W
13232		 && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS)
13233		 && ZEND_TYPE_IS_SET(prop_info->type)) {
13234			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
13235
13236			if (flags == ZEND_FETCH_DIM_WRITE) {
13237				if ((ZEND_TYPE_FULL_MASK(prop_info->type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) == 0) {
13238					if (!type_loaded) {
13239						type_loaded = 1;
13240						|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13241					}
13242					|	cmp dl, IS_FALSE
13243					|	jle >1
13244					|.cold_code
13245					|1:
13246					if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13247						|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13248					}
13249					|	LOAD_ADDR FCARG2a, prop_info
13250					|	SET_EX_OPLINE opline, r0
13251					|	EXT_CALL zend_jit_check_array_promotion, r0
13252					|	jmp >9
13253					|.code
13254				}
13255			} else if (flags == ZEND_FETCH_REF) {
13256				if (!type_loaded) {
13257					type_loaded = 1;
13258					|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13259				}
13260				|	IF_TYPE dl, IS_REFERENCE, >1
13261				if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
13262					|	LOAD_ADDR FCARG2a, prop_info
13263				} else {
13264					int prop_info_offset =
13265						(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
13266
13267					|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
13268					|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
13269					|	mov FCARG2a, aword[r0 + prop_info_offset]
13270				}
13271				if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13272					|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13273				}
13274				|.if X64
13275					|	LOAD_ZVAL_ADDR CARG3, res_addr
13276				|.else
13277					|	sub r4, 12
13278					|	PUSH_ZVAL_ADDR res_addr, r0
13279				|.endif
13280				|	EXT_CALL zend_jit_create_typed_ref, r0
13281				|.if not(X64)
13282				|	add r4, 12
13283				|.endif
13284				|	jmp >9
13285				|1:
13286			} else {
13287				ZEND_UNREACHABLE();
13288			}
13289		}
13290	}
13291	if (opline->opcode == ZEND_FETCH_OBJ_W) {
13292		if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13293			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13294		}
13295		|	SET_ZVAL_PTR res_addr, FCARG1a
13296		|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
13297		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && prop_info) {
13298			ssa->var_info[ssa_op->result_def].indirect_reference = 1;
13299		}
13300	} else {
13301		bool result_avoid_refcounting = 0;
13302
13303		if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) {
13304			uint32_t flags = 0;
13305			uint32_t old_info;
13306			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
13307			int32_t exit_point;
13308			const void *exit_addr;
13309			zend_uchar type;
13310			zend_jit_addr val_addr = prop_addr;
13311
13312			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
13313			 && !delayed_fetch_this
13314			 && !op1_avoid_refcounting) {
13315				flags = ZEND_JIT_EXIT_FREE_OP1;
13316			}
13317
13318			if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
13319			 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
13320			 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
13321			 && (ssa_op+1)->op1_use == ssa_op->result_def
13322			 && zend_jit_may_avoid_refcounting(opline+1, res_info)) {
13323				result_avoid_refcounting = 1;
13324				ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
13325			}
13326
13327			type = concrete_type(res_info);
13328
13329			if (prop_type != IS_UNKNOWN
13330			 && prop_type != IS_UNDEF
13331			 && prop_type != IS_REFERENCE
13332			 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT) {
13333				exit_point = zend_jit_trace_get_exit_point(opline, 0);
13334				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13335				if (!exit_addr) {
13336					return 0;
13337				}
13338			} else {
13339				val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
13340				|	LOAD_ZVAL_ADDR r0, prop_addr
13341				if (op1_avoid_refcounting) {
13342					SET_STACK_REG(JIT_G(current_frame)->stack,
13343						EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
13344				}
13345				old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
13346				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
13347				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
13348				exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
13349					SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
13350				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13351				if (!exit_addr) {
13352					return 0;
13353				}
13354
13355				if (!type_loaded) {
13356					type_loaded = 1;
13357					|	mov edx, dword [FCARG1a + prop_info->offset + 8]
13358				}
13359				|	// ZVAL_DEREF()
13360				|	IF_NOT_TYPE dl, IS_REFERENCE, >1
13361				|	GET_Z_PTR r0, r0
13362				|	add r0, offsetof(zend_reference, val)
13363				|	GET_ZVAL_TYPE_INFO edx, val_addr
13364			}
13365			res_info &= ~MAY_BE_GUARD;
13366			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
13367			if (type < IS_STRING) {
13368				|1:
13369				if (type_loaded) {
13370					|	IF_NOT_TYPE dl, type, &exit_addr
13371				} else {
13372					|	IF_NOT_ZVAL_TYPE val_addr, type, &exit_addr
13373				}
13374			} else {
13375				if (!type_loaded) {
13376					type_loaded = 1;
13377					|	GET_ZVAL_TYPE_INFO edx, val_addr
13378				}
13379				|1:
13380				|	IF_NOT_TYPE dl, type, &exit_addr
13381			}
13382			|	// ZVAL_COPY
13383			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_R0, ZREG_R1
13384			if (type < IS_STRING) {
13385				if (Z_REG(res_addr) != ZREG_FP ||
13386				    JIT_G(current_frame) == NULL ||
13387				    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
13388					|	SET_ZVAL_TYPE_INFO res_addr, type
13389				}
13390			} else {
13391				|	SET_ZVAL_TYPE_INFO res_addr, edx
13392				if (!result_avoid_refcounting) {
13393					|	TRY_ADDREF res_info, dh, r1
13394				}
13395			}
13396		} else {
13397			if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_R2)) {
13398				return 0;
13399			}
13400		}
13401	}
13402
13403	if (op1_avoid_refcounting) {
13404		SET_STACK_REG(JIT_G(current_frame)->stack,
13405			EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
13406	}
13407
13408	|.cold_code
13409
13410	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || !prop_info) {
13411		|5:
13412		|	SET_EX_OPLINE opline, r0
13413		if (opline->opcode == ZEND_FETCH_OBJ_W) {
13414			|	EXT_CALL zend_jit_fetch_obj_w_slow, r0
13415		} else if (opline->opcode != ZEND_FETCH_OBJ_IS) {
13416			|	EXT_CALL zend_jit_fetch_obj_r_slow, r0
13417		} else {
13418			|	EXT_CALL zend_jit_fetch_obj_is_slow, r0
13419		}
13420		|	jmp >9
13421	}
13422
13423	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)- MAY_BE_OBJECT)) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
13424		|7:
13425		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
13426			|	SET_EX_OPLINE opline, r0
13427			if (opline->opcode != ZEND_FETCH_OBJ_W
13428			 && (op1_info & MAY_BE_UNDEF)) {
13429				zend_jit_addr orig_op1_addr = OP1_ADDR();
13430
13431				if (op1_info & MAY_BE_ANY) {
13432					|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
13433				}
13434				|	mov FCARG1d, opline->op1.var
13435				|	EXT_CALL zend_jit_undefined_op_helper, r0
13436				|1:
13437				|	LOAD_ZVAL_ADDR FCARG1a, orig_op1_addr
13438			} else if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13439				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13440			}
13441			|	LOAD_ADDR FCARG2a, Z_STRVAL_P(member)
13442			if (opline->opcode == ZEND_FETCH_OBJ_W) {
13443				|	EXT_CALL zend_jit_invalid_property_write, r0
13444				|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR
13445			} else {
13446				|	EXT_CALL zend_jit_invalid_property_read, r0
13447				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
13448			}
13449			|	jmp >9
13450		} else {
13451			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
13452			|	jmp >9
13453		}
13454	}
13455
13456	if (!prop_info
13457	 && may_be_dynamic
13458	 && opline->opcode != ZEND_FETCH_OBJ_W) {
13459		|8:
13460		|	mov FCARG2a, r0
13461		|	SET_EX_OPLINE opline, r0
13462		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
13463			|	EXT_CALL zend_jit_fetch_obj_r_dynamic, r0
13464		} else {
13465			|	EXT_CALL zend_jit_fetch_obj_is_dynamic, r0
13466		}
13467		|	jmp >9
13468	}
13469
13470	|.code;
13471	|9: // END
13472	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
13473		if (opline->op1_type == IS_VAR
13474		 && opline->opcode == ZEND_FETCH_OBJ_W
13475		 && (op1_info & MAY_BE_RC1)) {
13476			zend_jit_addr orig_op1_addr = OP1_ADDR();
13477
13478			|	IF_NOT_ZVAL_REFCOUNTED orig_op1_addr, >1
13479			|	GET_ZVAL_PTR FCARG1a, orig_op1_addr
13480			|	GC_DELREF FCARG1a
13481			|	jnz >1
13482			|	SET_EX_OPLINE opline, r0
13483			|	EXT_CALL zend_jit_extract_helper, r0
13484			|1:
13485		} else if (!op1_avoid_refcounting) {
13486			if (on_this) {
13487				op1_info &= ~MAY_BE_RC1;
13488			}
13489			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
13490		}
13491	}
13492
13493	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
13494	 && prop_info
13495	 && (opline->opcode != ZEND_FETCH_OBJ_W ||
13496	     !(opline->extended_value & ZEND_FETCH_OBJ_FLAGS) ||
13497	     !ZEND_TYPE_IS_SET(prop_info->type))
13498	 && (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || on_this || op1_indirect)) {
13499		may_throw = 0;
13500	}
13501
13502	if (may_throw) {
13503		if (!zend_jit_check_exception(Dst)) {
13504			return 0;
13505		}
13506	}
13507
13508	return 1;
13509}
13510
13511static int zend_jit_incdec_obj(dasm_State          **Dst,
13512                               const zend_op        *opline,
13513                               const zend_op_array  *op_array,
13514                               zend_ssa             *ssa,
13515                               const zend_ssa_op    *ssa_op,
13516                               uint32_t              op1_info,
13517                               zend_jit_addr         op1_addr,
13518                               bool                  op1_indirect,
13519                               zend_class_entry     *ce,
13520                               bool                  ce_is_instanceof,
13521                               bool                  on_this,
13522                               bool                  delayed_fetch_this,
13523                               zend_class_entry     *trace_ce,
13524                               uint8_t               prop_type)
13525{
13526	zval *member;
13527	zend_string *name;
13528	zend_property_info *prop_info;
13529	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
13530	zend_jit_addr res_addr = 0;
13531	zend_jit_addr prop_addr;
13532	bool needs_slow_path = 0;
13533	bool use_prop_guard = 0;
13534	bool may_throw = 0;
13535	uint32_t res_info = (opline->result_type != IS_UNDEF) ? RES_INFO() : 0;
13536
13537	ZEND_ASSERT(opline->op2_type == IS_CONST);
13538	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
13539
13540	if (opline->result_type != IS_UNUSED) {
13541		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
13542	}
13543
13544	member = RT_CONSTANT(opline, opline->op2);
13545	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
13546	name = Z_STR_P(member);
13547	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
13548
13549	if (on_this) {
13550		|	GET_ZVAL_PTR FCARG1a, this_addr
13551	} else {
13552		if (opline->op1_type == IS_VAR
13553		 && (op1_info & MAY_BE_INDIRECT)
13554		 && Z_REG(op1_addr) == ZREG_FP) {
13555			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13556			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
13557			|	GET_Z_PTR FCARG1a, FCARG1a
13558			|1:
13559			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13560		}
13561		if (op1_info & MAY_BE_REF) {
13562			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13563				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13564			}
13565			|	ZVAL_DEREF FCARG1a, op1_info
13566			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13567		}
13568		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
13569			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13570				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13571				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13572
13573				if (!exit_addr) {
13574					return 0;
13575				}
13576				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
13577			} else {
13578				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
13579				|.cold_code
13580				|1:
13581				|	SET_EX_OPLINE opline, r0
13582				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13583					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
13584				}
13585				|	LOAD_ADDR FCARG2a, ZSTR_VAL(name)
13586				|	EXT_CALL zend_jit_invalid_property_incdec, r0
13587				|	jmp ->exception_handler
13588				|.code
13589			}
13590		}
13591		|	GET_ZVAL_PTR FCARG1a, op1_addr
13592	}
13593
13594	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
13595		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
13596		if (prop_info) {
13597			ce = trace_ce;
13598			ce_is_instanceof = 0;
13599			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
13600				if (on_this && JIT_G(current_frame)
13601				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
13602					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
13603				} else if (zend_jit_class_guard(Dst, opline, ce)) {
13604					if (on_this && JIT_G(current_frame)) {
13605						JIT_G(current_frame)->ce = ce;
13606						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
13607					}
13608				} else {
13609					return 0;
13610				}
13611				if (ssa->var_info && ssa_op->op1_use >= 0) {
13612					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
13613					ssa->var_info[ssa_op->op1_use].ce = ce;
13614					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
13615				}
13616				if (ssa->var_info && ssa_op->op1_def >= 0) {
13617					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
13618					ssa->var_info[ssa_op->op1_def].ce = ce;
13619					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
13620				}
13621			}
13622		}
13623	}
13624
13625	use_prop_guard = (prop_type != IS_UNKNOWN
13626		&& prop_type != IS_UNDEF
13627		&& prop_type != IS_REFERENCE
13628		&& (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT);
13629
13630	if (!prop_info) {
13631		needs_slow_path = 1;
13632
13633		|	mov r0, EX->run_time_cache
13634		|	mov r2, aword [r0 + opline->extended_value]
13635		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
13636		|	jne >7
13637		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
13638			|	cmp aword [r0 + opline->extended_value + sizeof(void*) * 2], 0
13639			|	jnz >7
13640		}
13641		|	mov r0, aword [r0 + opline->extended_value + sizeof(void*)]
13642		|	test r0, r0
13643		|	jl >7
13644		if (!use_prop_guard) {
13645			|	IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >7
13646		}
13647		|	add FCARG1a, r0
13648		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13649	} else {
13650		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
13651		if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) {
13652			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13653				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13654				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13655
13656				if (!exit_addr) {
13657					return 0;
13658				}
13659				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr
13660			} else {
13661				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >7
13662				needs_slow_path = 1;
13663			}
13664		}
13665		if (ZEND_TYPE_IS_SET(prop_info->type)) {
13666			may_throw = 1;
13667			|	SET_EX_OPLINE opline, r0
13668			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
13669				|	LOAD_ADDR FCARG2a, prop_info
13670			} else {
13671				int prop_info_offset =
13672					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
13673
13674				|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
13675				|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
13676				|	mov FCARG2a, aword[r0 + prop_info_offset]
13677			}
13678			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13679			if (opline->result_type == IS_UNUSED) {
13680				switch (opline->opcode) {
13681					case ZEND_PRE_INC_OBJ:
13682					case ZEND_POST_INC_OBJ:
13683						|	EXT_CALL zend_jit_inc_typed_prop, r0
13684						break;
13685					case ZEND_PRE_DEC_OBJ:
13686					case ZEND_POST_DEC_OBJ:
13687						|	EXT_CALL zend_jit_dec_typed_prop, r0
13688						break;
13689					default:
13690						ZEND_UNREACHABLE();
13691				}
13692			} else {
13693				|.if X64
13694					|	LOAD_ZVAL_ADDR CARG3, res_addr
13695				|.else
13696					|	sub r4, 12
13697					|	PUSH_ZVAL_ADDR res_addr, r0
13698				|.endif
13699				switch (opline->opcode) {
13700					case ZEND_PRE_INC_OBJ:
13701						|	EXT_CALL zend_jit_pre_inc_typed_prop, r0
13702						break;
13703					case ZEND_PRE_DEC_OBJ:
13704						|	EXT_CALL zend_jit_pre_dec_typed_prop, r0
13705						break;
13706					case ZEND_POST_INC_OBJ:
13707						|	EXT_CALL zend_jit_post_inc_typed_prop, r0
13708						break;
13709					case ZEND_POST_DEC_OBJ:
13710						|	EXT_CALL zend_jit_post_dec_typed_prop, r0
13711						break;
13712					default:
13713						ZEND_UNREACHABLE();
13714				}
13715				|.if not(X64)
13716					|	add r4, 12
13717				|.endif
13718			}
13719		}
13720	}
13721
13722	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
13723		uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
13724		zend_jit_addr var_addr = prop_addr;
13725
13726		if (use_prop_guard) {
13727			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
13728			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13729			if (!exit_addr) {
13730				return 0;
13731			}
13732
13733			|	IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr
13734			var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
13735		}
13736
13737		if (var_info & MAY_BE_REF) {
13738			may_throw = 1;
13739			var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13740			if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
13741				|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13742			}
13743			|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2
13744			|	GET_ZVAL_PTR FCARG1a, var_addr
13745			|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
13746			|	jnz >1
13747			|	lea FCARG1a, aword [FCARG1a + offsetof(zend_reference, val)]
13748			|.cold_code
13749			|1:
13750			if (opline) {
13751				|	SET_EX_OPLINE opline, r0
13752			}
13753			if (opline->result_type == IS_UNUSED) {
13754				|	xor FCARG2a, FCARG2a
13755			} else {
13756				|	LOAD_ZVAL_ADDR FCARG2a, res_addr
13757			}
13758			switch (opline->opcode) {
13759				case ZEND_PRE_INC_OBJ:
13760					|	EXT_CALL zend_jit_pre_inc_typed_ref, r0
13761					break;
13762				case ZEND_PRE_DEC_OBJ:
13763					|	EXT_CALL zend_jit_pre_dec_typed_ref, r0
13764					break;
13765				case ZEND_POST_INC_OBJ:
13766					|	EXT_CALL zend_jit_post_inc_typed_ref, r0
13767					break;
13768				case ZEND_POST_DEC_OBJ:
13769					|	EXT_CALL zend_jit_post_dec_typed_ref, r0
13770					break;
13771				default:
13772					ZEND_UNREACHABLE();
13773			}
13774			|	jmp >9
13775			|.code
13776			|2:
13777		}
13778
13779		if (var_info & MAY_BE_LONG) {
13780			if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) {
13781				|	IF_NOT_ZVAL_TYPE var_addr, IS_LONG, >2
13782			}
13783			if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
13784				if (opline->result_type != IS_UNUSED) {
13785					|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_R1, ZREG_R2
13786				}
13787			}
13788			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13789				|	LONG_OP_WITH_32BIT_CONST add, var_addr, Z_L(1)
13790			} else {
13791				|	LONG_OP_WITH_32BIT_CONST sub, var_addr, Z_L(1)
13792			}
13793			|	jo	>3
13794			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) {
13795				if (opline->result_type != IS_UNUSED) {
13796					|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_R0, ZREG_R2
13797				}
13798			}
13799			|.cold_code
13800		}
13801		if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) {
13802			if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
13803				may_throw = 1;
13804			}
13805			if (var_info & MAY_BE_LONG) {
13806				|2:
13807			}
13808			if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
13809				var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13810				|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
13811			}
13812			if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
13813				|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_ANY, ZREG_R0, ZREG_R2
13814				|	TRY_ADDREF MAY_BE_ANY, ah, r2
13815			}
13816			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13817				if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13818					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
13819					|	EXT_CALL zend_jit_pre_inc, r0
13820				} else {
13821					|	EXT_CALL increment_function, r0
13822				}
13823			} else {
13824				if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13825					|	LOAD_ZVAL_ADDR FCARG2a, res_addr
13826					|	EXT_CALL zend_jit_pre_dec, r0
13827				} else {
13828					|	EXT_CALL decrement_function, r0
13829				}
13830			}
13831			if (var_info & MAY_BE_LONG) {
13832				|	jmp >4
13833			}
13834		}
13835		if (var_info & MAY_BE_LONG) {
13836			|3:
13837			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13838				|.if X64
13839					|	mov64 rax, 0x43e0000000000000
13840					|	SET_ZVAL_LVAL var_addr, rax
13841					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13842					if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13843						|	SET_ZVAL_LVAL res_addr, rax
13844						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13845					}
13846				|.else
13847					|	SET_ZVAL_LVAL var_addr, 0
13848					|	SET_ZVAL_W2 var_addr, 0x41e00000
13849					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13850					if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13851						|	SET_ZVAL_LVAL res_addr, 0
13852						|	SET_ZVAL_W2 res_addr, 0x41e00000
13853						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13854					}
13855				|.endif
13856			} else {
13857				|.if X64
13858					|	mov64 rax, 0xc3e0000000000000
13859					|	SET_ZVAL_LVAL var_addr, rax
13860					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13861					if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13862						|	SET_ZVAL_LVAL res_addr, rax
13863						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13864					}
13865				|.else
13866					|	SET_ZVAL_LVAL var_addr, 0x00200000
13867					|	SET_ZVAL_W2 var_addr, 0xc1e00000
13868					|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE
13869					if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13870						|	SET_ZVAL_LVAL res_addr, 0x00200000
13871						|	SET_ZVAL_W2 res_addr, 0xc1e00000
13872						|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE
13873					}
13874				|.endif
13875			}
13876			if (opline->result_type != IS_UNUSED
13877			 && (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ)
13878			 && prop_info
13879			 && !ZEND_TYPE_IS_SET(prop_info->type)
13880			 && (res_info & MAY_BE_GUARD)
13881			 && (res_info & MAY_BE_LONG)) {
13882				zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
13883				uint32_t old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
13884				int32_t exit_point;
13885				const void *exit_addr;
13886
13887				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
13888				exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
13889				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13890				if (!exit_addr) {
13891					return 0;
13892				}
13893				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
13894				ssa->var_info[ssa_op->result_def].type = res_info & ~MAY_BE_GUARD;
13895				|	jmp &exit_addr
13896				|.code
13897			} else {
13898				|	jmp >4
13899				|.code
13900				|4:
13901			}
13902		}
13903	}
13904
13905	if (needs_slow_path) {
13906		may_throw = 1;
13907		|.cold_code
13908		|7:
13909		|	SET_EX_OPLINE opline, r0
13910		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
13911		|	LOAD_ADDR FCARG2a, name
13912		|.if X64
13913			|	mov CARG3, EX->run_time_cache
13914			|	add CARG3, opline->extended_value
13915			if (opline->result_type == IS_UNUSED) {
13916				|	xor CARG4, CARG4
13917			} else {
13918				|	LOAD_ZVAL_ADDR CARG4, res_addr
13919			}
13920		|.else
13921			|	sub r4, 8
13922			if (opline->result_type == IS_UNUSED) {
13923				|	push 0
13924			} else {
13925				|	PUSH_ZVAL_ADDR res_addr, r0
13926			}
13927			|	mov r0, EX->run_time_cache
13928			|	add r0, opline->extended_value
13929			|	push r0
13930		|.endif
13931
13932		switch (opline->opcode) {
13933			case ZEND_PRE_INC_OBJ:
13934				|	EXT_CALL zend_jit_pre_inc_obj_helper, r0
13935				break;
13936			case ZEND_PRE_DEC_OBJ:
13937				|	EXT_CALL zend_jit_pre_dec_obj_helper, r0
13938				break;
13939			case ZEND_POST_INC_OBJ:
13940				|	EXT_CALL zend_jit_post_inc_obj_helper, r0
13941				break;
13942			case ZEND_POST_DEC_OBJ:
13943				|	EXT_CALL zend_jit_post_dec_obj_helper, r0
13944				break;
13945			default:
13946				ZEND_UNREACHABLE();
13947		}
13948
13949		|.if not(X64)
13950			|	add r4, 8
13951		|.endif
13952
13953		|	jmp >9
13954		|.code
13955	}
13956
13957	|9:
13958	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
13959		if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
13960			may_throw = 1;
13961		}
13962		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
13963	}
13964
13965	if (may_throw) {
13966		if (!zend_jit_check_exception(Dst)) {
13967			return 0;
13968		}
13969	}
13970
13971	return 1;
13972}
13973
13974static int zend_jit_assign_obj_op(dasm_State          **Dst,
13975                                  const zend_op        *opline,
13976                                  const zend_op_array  *op_array,
13977                                  zend_ssa             *ssa,
13978                                  const zend_ssa_op    *ssa_op,
13979                                  uint32_t              op1_info,
13980                                  zend_jit_addr         op1_addr,
13981                                  uint32_t              val_info,
13982                                  zend_ssa_range       *val_range,
13983                                  bool                  op1_indirect,
13984                                  zend_class_entry     *ce,
13985                                  bool                  ce_is_instanceof,
13986                                  bool                  on_this,
13987                                  bool                  delayed_fetch_this,
13988                                  zend_class_entry     *trace_ce,
13989                                  uint8_t               prop_type)
13990{
13991	zval *member;
13992	zend_string *name;
13993	zend_property_info *prop_info;
13994	zend_jit_addr val_addr = OP1_DATA_ADDR();
13995	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
13996	zend_jit_addr prop_addr;
13997	bool needs_slow_path = 0;
13998	bool use_prop_guard = 0;
13999	bool may_throw = 0;
14000	binary_op_type binary_op = get_binary_op(opline->extended_value);
14001
14002	ZEND_ASSERT(opline->op2_type == IS_CONST);
14003	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
14004	ZEND_ASSERT(opline->result_type == IS_UNUSED);
14005
14006	member = RT_CONSTANT(opline, opline->op2);
14007	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
14008	name = Z_STR_P(member);
14009	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
14010
14011	if (on_this) {
14012		|	GET_ZVAL_PTR FCARG1a, this_addr
14013	} else {
14014		if (opline->op1_type == IS_VAR
14015		 && (op1_info & MAY_BE_INDIRECT)
14016		 && Z_REG(op1_addr) == ZREG_FP) {
14017			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14018			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
14019			|	GET_Z_PTR FCARG1a, FCARG1a
14020			|1:
14021			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14022		}
14023		if (op1_info & MAY_BE_REF) {
14024			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
14025				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14026			}
14027			|	ZVAL_DEREF FCARG1a, op1_info
14028			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14029		}
14030		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
14031			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14032				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14033				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14034
14035				if (!exit_addr) {
14036					return 0;
14037				}
14038				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
14039			} else {
14040				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
14041				|.cold_code
14042				|1:
14043				|	SET_EX_OPLINE opline, r0
14044				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
14045					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14046				}
14047				|	LOAD_ADDR FCARG2a, ZSTR_VAL(name)
14048				if (op1_info & MAY_BE_UNDEF) {
14049					|	EXT_CALL zend_jit_invalid_property_assign_op, r0
14050				} else {
14051					|	EXT_CALL zend_jit_invalid_property_assign, r0
14052				}
14053				may_throw = 1;
14054				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
14055				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14056					may_throw = 1;
14057					|	jmp >8
14058				} else {
14059					|	jmp >9
14060				}
14061				|.code
14062			}
14063		}
14064		|	GET_ZVAL_PTR FCARG1a, op1_addr
14065	}
14066
14067	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
14068		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
14069		if (prop_info) {
14070			ce = trace_ce;
14071			ce_is_instanceof = 0;
14072			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
14073				if (on_this && JIT_G(current_frame)
14074				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
14075					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
14076				} else if (zend_jit_class_guard(Dst, opline, ce)) {
14077					if (on_this && JIT_G(current_frame)) {
14078						JIT_G(current_frame)->ce = ce;
14079						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
14080					}
14081				} else {
14082					return 0;
14083				}
14084				if (ssa->var_info && ssa_op->op1_use >= 0) {
14085					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
14086					ssa->var_info[ssa_op->op1_use].ce = ce;
14087					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
14088				}
14089				if (ssa->var_info && ssa_op->op1_def >= 0) {
14090					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
14091					ssa->var_info[ssa_op->op1_def].ce = ce;
14092					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
14093				}
14094			}
14095		}
14096	}
14097
14098	use_prop_guard = (prop_type != IS_UNKNOWN
14099		&& prop_type != IS_UNDEF
14100		&& prop_type != IS_REFERENCE
14101		&& (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT);
14102
14103	if (!prop_info) {
14104		needs_slow_path = 1;
14105
14106		|	mov r0, EX->run_time_cache
14107		|	mov r2, aword [r0 + (opline+1)->extended_value]
14108		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
14109		|	jne >7
14110		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
14111			|	cmp aword [r0 + (opline+1)->extended_value + sizeof(void*) * 2], 0
14112			|	jnz >7
14113		}
14114		|	mov r0, aword [r0 + (opline+1)->extended_value + sizeof(void*)]
14115		|	test r0, r0
14116		|	jl >7
14117		if (!use_prop_guard) {
14118			|	IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >7
14119		}
14120		|	add FCARG1a, r0
14121		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14122	} else {
14123		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
14124		if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) {
14125			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14126				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14127				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14128
14129				if (!exit_addr) {
14130					return 0;
14131				}
14132				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr
14133			} else {
14134				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >7
14135				needs_slow_path = 1;
14136			}
14137		}
14138		if (ZEND_TYPE_IS_SET(prop_info->type)) {
14139			uint32_t info = val_info;
14140
14141			may_throw = 1;
14142
14143			if (opline) {
14144				|	SET_EX_OPLINE opline, r0
14145			}
14146
14147			|	IF_ZVAL_TYPE prop_addr, IS_REFERENCE, >1
14148			|.cold_code
14149			|1:
14150			|	GET_ZVAL_PTR FCARG1a, prop_addr
14151			if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
14152				|	LOAD_ZVAL_ADDR FCARG2a, val_addr
14153			}
14154			|.if X64
14155				|	LOAD_ADDR CARG3, binary_op
14156			|.else
14157				|	sub r4, 12
14158				|	PUSH_ADDR binary_op, r0
14159			|.endif
14160			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
14161			 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14162				|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
14163			} else {
14164				|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
14165			}
14166			|.if not(X64)
14167				|	add r4, 12
14168			|.endif
14169			|	jmp >9
14170			|.code
14171
14172			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
14173
14174			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
14175				|	LOAD_ADDR FCARG2a, prop_info
14176			} else {
14177				int prop_info_offset =
14178					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
14179
14180				|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
14181				|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
14182				|	mov FCARG2a, aword[r0 + prop_info_offset]
14183			}
14184			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
14185			|.if X64
14186				|	LOAD_ZVAL_ADDR CARG3, val_addr
14187				|	LOAD_ADDR CARG4, binary_op
14188			|.else
14189				|	sub r4, 8
14190				|	PUSH_ADDR binary_op, r0
14191				|	PUSH_ZVAL_ADDR val_addr, r0
14192			|.endif
14193
14194			|	EXT_CALL zend_jit_assign_op_to_typed_prop, r0
14195
14196			|.if not(X64)
14197				|	add r4, 8
14198			|.endif
14199
14200			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14201				info |= MAY_BE_RC1|MAY_BE_RCN;
14202			}
14203
14204			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL
14205		}
14206	}
14207
14208	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
14209		zend_jit_addr var_addr = prop_addr;
14210		uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
14211		uint32_t var_def_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
14212
14213		if (use_prop_guard) {
14214			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
14215			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14216			if (!exit_addr) {
14217				return 0;
14218			}
14219
14220			|	IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr
14221			var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
14222		}
14223
14224		if (var_info & MAY_BE_REF) {
14225			may_throw = 1;
14226			var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
14227			|	LOAD_ZVAL_ADDR r0, prop_addr
14228			|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2
14229			|	GET_ZVAL_PTR FCARG1a, var_addr
14230			|	cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
14231			|	jnz >1
14232			|	lea r0, aword [FCARG1a + offsetof(zend_reference, val)]
14233			|.cold_code
14234			|1:
14235			if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
14236				|	LOAD_ZVAL_ADDR FCARG2a, val_addr
14237			}
14238			if (opline) {
14239				|	SET_EX_OPLINE opline, r0
14240			}
14241			|.if X64
14242				|	LOAD_ADDR CARG3, binary_op
14243			|.else
14244				|	sub r4, 12
14245				|	PUSH_ADDR binary_op, r0
14246			|.endif
14247			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
14248			 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14249				|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, r0
14250			} else {
14251				|	EXT_CALL zend_jit_assign_op_to_typed_ref, r0
14252			}
14253			|.if not(X64)
14254				|	add r4, 12
14255			|.endif
14256			|	jmp >9
14257			|.code
14258			|2:
14259			var_info &= ~MAY_BE_REF;
14260		}
14261
14262		switch (opline->extended_value) {
14263			case ZEND_ADD:
14264			case ZEND_SUB:
14265			case ZEND_MUL:
14266				if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14267				    (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14268					if (opline->extended_value != ZEND_ADD ||
14269					    (var_info & MAY_BE_ANY) != MAY_BE_ARRAY ||
14270					    (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) {
14271						may_throw = 1;
14272					}
14273				}
14274				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,
14275						1 /* may overflow */, 0)) {
14276					return 0;
14277				}
14278				break;
14279			case ZEND_BW_OR:
14280			case ZEND_BW_AND:
14281			case ZEND_BW_XOR:
14282				may_throw = 1;
14283				if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14284				    (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14285					if ((var_info & MAY_BE_ANY) != MAY_BE_STRING ||
14286					    (val_info & MAY_BE_ANY) != MAY_BE_STRING) {
14287						may_throw = 1;
14288					}
14289				}
14290				goto long_math;
14291			case ZEND_SL:
14292			case ZEND_SR:
14293				if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14294				    (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14295					may_throw = 1;
14296				}
14297				if ((opline+1)->op1_type != IS_CONST ||
14298				    Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG ||
14299				    Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) < 0) {
14300					may_throw = 1;
14301				}
14302				goto long_math;
14303			case ZEND_MOD:
14304				if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
14305				    (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14306					if (opline->extended_value != ZEND_ADD ||
14307					    (var_info & MAY_BE_ANY) != MAY_BE_ARRAY ||
14308					    (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) {
14309						may_throw = 1;
14310					}
14311				}
14312				if ((opline+1)->op1_type != IS_CONST ||
14313				    Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG ||
14314				    Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) == 0) {
14315					may_throw = 1;
14316				}
14317long_math:
14318				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
14319						IS_CV, opline->op1, var_addr, var_info, NULL,
14320						(opline+1)->op1_type, (opline+1)->op1, val_addr, val_info,
14321						val_range,
14322						0, var_addr, var_def_info, var_info, /* may throw */ 1)) {
14323					return 0;
14324				}
14325				break;
14326			case ZEND_CONCAT:
14327				may_throw = 1;
14328				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,
14329						0)) {
14330					return 0;
14331				}
14332				break;
14333			default:
14334				ZEND_UNREACHABLE();
14335		}
14336	}
14337
14338	if (needs_slow_path) {
14339		may_throw = 1;
14340		|.cold_code
14341		|7:
14342		|	SET_EX_OPLINE opline, r0
14343		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
14344		|	LOAD_ADDR FCARG2a, name
14345		|.if X64
14346			|	LOAD_ZVAL_ADDR CARG3, val_addr
14347			|	mov CARG4, EX->run_time_cache
14348			|	add CARG4, (opline+1)->extended_value
14349			|.if X64WIN
14350			|	LOAD_ADDR r0, binary_op
14351			|	mov aword A5, r0
14352			|.else
14353			|	LOAD_ADDR CARG5, binary_op
14354			|.endif
14355		|.else
14356			|	sub r4, 4
14357			|	PUSH_ADDR binary_op, r0
14358			|	mov r0, EX->run_time_cache
14359			|	add r0, (opline+1)->extended_value
14360			|	push r0
14361			|	PUSH_ZVAL_ADDR val_addr, r0
14362		|.endif
14363
14364		|	EXT_CALL zend_jit_assign_obj_op_helper, r0
14365
14366		|.if not(X64)
14367			|	add r4, 4
14368		|.endif
14369
14370		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14371			val_info |= MAY_BE_RC1|MAY_BE_RCN;
14372		}
14373
14374		|8:
14375		|	// FREE_OP_DATA();
14376		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline
14377		|	jmp >9
14378		|.code
14379	}
14380
14381	|9:
14382	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
14383		if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
14384			may_throw = 1;
14385		}
14386		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
14387	}
14388
14389	if (may_throw) {
14390		if (!zend_jit_check_exception(Dst)) {
14391			return 0;
14392		}
14393	}
14394
14395	return 1;
14396}
14397
14398static int zend_jit_assign_obj(dasm_State          **Dst,
14399                               const zend_op        *opline,
14400                               const zend_op_array  *op_array,
14401                               zend_ssa             *ssa,
14402                               const zend_ssa_op    *ssa_op,
14403                               uint32_t              op1_info,
14404                               zend_jit_addr         op1_addr,
14405                               uint32_t              val_info,
14406                               bool                  op1_indirect,
14407                               zend_class_entry     *ce,
14408                               bool                  ce_is_instanceof,
14409                               bool                  on_this,
14410                               bool                  delayed_fetch_this,
14411                               zend_class_entry     *trace_ce,
14412                               uint8_t               prop_type,
14413                               int                   may_throw)
14414{
14415	zval *member;
14416	zend_string *name;
14417	zend_property_info *prop_info;
14418	zend_jit_addr val_addr = OP1_DATA_ADDR();
14419	zend_jit_addr res_addr = 0;
14420	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
14421	zend_jit_addr prop_addr;
14422	bool needs_slow_path = 0;
14423	bool needs_val_dtor = 0;
14424
14425	if (RETURN_VALUE_USED(opline)) {
14426		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
14427	}
14428
14429	ZEND_ASSERT(opline->op2_type == IS_CONST);
14430	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
14431
14432	member = RT_CONSTANT(opline, opline->op2);
14433	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
14434	name = Z_STR_P(member);
14435	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
14436
14437	if (on_this) {
14438		|	GET_ZVAL_PTR FCARG1a, this_addr
14439	} else {
14440		if (opline->op1_type == IS_VAR
14441		 && (op1_info & MAY_BE_INDIRECT)
14442		 && Z_REG(op1_addr) == ZREG_FP) {
14443			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14444			|	IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1
14445			|	GET_Z_PTR FCARG1a, FCARG1a
14446			|1:
14447			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14448		}
14449		if (op1_info & MAY_BE_REF) {
14450			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
14451				|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14452			}
14453			|	ZVAL_DEREF FCARG1a, op1_info
14454			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14455		}
14456		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
14457			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14458				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14459				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14460
14461				if (!exit_addr) {
14462					return 0;
14463				}
14464				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr
14465			} else {
14466				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1
14467				|.cold_code
14468				|1:
14469				|	SET_EX_OPLINE opline, r0
14470				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
14471					|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
14472				}
14473				|	LOAD_ADDR FCARG2a, ZSTR_VAL(name)
14474				|	EXT_CALL zend_jit_invalid_property_assign, r0
14475				if (RETURN_VALUE_USED(opline)) {
14476					|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL
14477				}
14478				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
14479				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14480				 	needs_val_dtor = 1;
14481					|	jmp >7
14482				} else {
14483					|	jmp >9
14484				}
14485				|.code
14486			}
14487		}
14488		|	GET_ZVAL_PTR FCARG1a, op1_addr
14489	}
14490
14491	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
14492		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
14493		if (prop_info) {
14494			ce = trace_ce;
14495			ce_is_instanceof = 0;
14496			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
14497				if (on_this && JIT_G(current_frame)
14498				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
14499					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
14500				} else if (zend_jit_class_guard(Dst, opline, ce)) {
14501					if (on_this && JIT_G(current_frame)) {
14502						JIT_G(current_frame)->ce = ce;
14503						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
14504					}
14505				} else {
14506					return 0;
14507				}
14508				if (ssa->var_info && ssa_op->op1_use >= 0) {
14509					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
14510					ssa->var_info[ssa_op->op1_use].ce = ce;
14511					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
14512				}
14513				if (ssa->var_info && ssa_op->op1_def >= 0) {
14514					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
14515					ssa->var_info[ssa_op->op1_def].ce = ce;
14516					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
14517				}
14518			}
14519		}
14520	}
14521
14522	if (!prop_info) {
14523		needs_slow_path = 1;
14524
14525		|	mov r0, EX->run_time_cache
14526		|	mov r2, aword [r0 + opline->extended_value]
14527		|	cmp r2, aword [FCARG1a + offsetof(zend_object, ce)]
14528		|	jne >5
14529		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
14530			|	mov FCARG2a, aword [r0 + opline->extended_value + sizeof(void*) * 2]
14531		}
14532		|	mov r0, aword [r0 + opline->extended_value + sizeof(void*)]
14533		|	test r0, r0
14534		|	jl >5
14535		|	IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >5
14536		|	add FCARG1a, r0
14537		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14538		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
14539			|	test FCARG2a, FCARG2a
14540			|	jnz >1
14541			|.cold_code
14542			|1:
14543			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
14544			|	SET_EX_OPLINE opline, r0
14545			|.if X64
14546				|	LOAD_ZVAL_ADDR CARG3, val_addr
14547				if (RETURN_VALUE_USED(opline)) {
14548					|	LOAD_ZVAL_ADDR CARG4, res_addr
14549				} else {
14550					|	xor CARG4, CARG4
14551				}
14552			|.else
14553				|	sub r4, 8
14554				if (RETURN_VALUE_USED(opline)) {
14555					|	PUSH_ZVAL_ADDR res_addr, r0
14556				} else {
14557					|	push 0
14558				}
14559				|	PUSH_ZVAL_ADDR val_addr, r0
14560			|.endif
14561
14562			|	EXT_CALL zend_jit_assign_to_typed_prop, r0
14563
14564			|.if not(X64)
14565				|	add r4, 8
14566			|.endif
14567
14568			if ((opline+1)->op1_type == IS_CONST) {
14569				|	// TODO: ???
14570				|	// if (Z_TYPE_P(value) == orig_type) {
14571				|	// CACHE_PTR_EX(cache_slot + 2, NULL);
14572			}
14573
14574			if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
14575			 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
14576				|	jmp >7
14577			} else {
14578				|	jmp >9
14579			}
14580			|.code
14581		}
14582	} else {
14583		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
14584		if (!ce || ce_is_instanceof || !(ce->ce_flags & ZEND_ACC_IMMUTABLE) || ce->__get || ce->__set || (prop_info->flags & ZEND_ACC_READONLY)) {
14585			// Undefined property with magic __get()/__set()
14586			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14587				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14588				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14589
14590				if (!exit_addr) {
14591					return 0;
14592				}
14593				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr
14594			} else {
14595				|	IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >5
14596				needs_slow_path = 1;
14597			}
14598		}
14599		if (ZEND_TYPE_IS_SET(prop_info->type)) {
14600			uint32_t info = val_info;
14601
14602			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
14603			|	SET_EX_OPLINE opline, r0
14604			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
14605				|	LOAD_ADDR FCARG2a, prop_info
14606			} else {
14607				int prop_info_offset =
14608					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
14609
14610				|	mov r0, aword [FCARG1a + offsetof(zend_object, ce)]
14611				|	mov	r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)]
14612				|	mov FCARG2a, aword[r0 + prop_info_offset]
14613			}
14614			|	LOAD_ZVAL_ADDR FCARG1a, prop_addr
14615			|.if X64
14616				|	LOAD_ZVAL_ADDR CARG3, val_addr
14617				if (RETURN_VALUE_USED(opline)) {
14618					|	LOAD_ZVAL_ADDR CARG4, res_addr
14619				} else {
14620					|	xor CARG4, CARG4
14621				}
14622			|.else
14623				|	sub r4, 8
14624				if (RETURN_VALUE_USED(opline)) {
14625					|	PUSH_ZVAL_ADDR res_addr, r0
14626				} else {
14627					|	push 0
14628				}
14629				|	PUSH_ZVAL_ADDR val_addr, r0
14630			|.endif
14631
14632			|	EXT_CALL zend_jit_assign_to_typed_prop, r0
14633
14634			|.if not(X64)
14635				|	add r4, 8
14636			|.endif
14637
14638			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14639				info |= MAY_BE_RC1|MAY_BE_RCN;
14640			}
14641
14642			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL
14643		}
14644	}
14645
14646	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
14647		// value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES());
14648		if (opline->result_type == IS_UNUSED) {
14649			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)) {
14650				return 0;
14651			}
14652		} else {
14653			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)) {
14654				return 0;
14655			}
14656		}
14657	}
14658
14659	if (needs_slow_path) {
14660		|.cold_code
14661		|5:
14662		|	SET_EX_OPLINE opline, r0
14663		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
14664		|	LOAD_ADDR FCARG2a, name
14665		|.if X64
14666			|	LOAD_ZVAL_ADDR CARG3, val_addr
14667			|	mov CARG4, EX->run_time_cache
14668			|	add CARG4, opline->extended_value
14669			if (RETURN_VALUE_USED(opline)) {
14670				|.if X64WIN
14671				|	LOAD_ZVAL_ADDR r0, res_addr
14672				|	mov aword A5, r0
14673				|.else
14674				|	LOAD_ZVAL_ADDR CARG5, res_addr
14675				|.endif
14676			} else {
14677				|.if X64WIN
14678				|	mov aword A5, 0
14679				|.else
14680				|	xor CARG5, CARG5
14681				|.endif
14682			}
14683		|.else
14684			|	sub r4, 4
14685			if (RETURN_VALUE_USED(opline)) {
14686				|	PUSH_ZVAL_ADDR res_addr, r0
14687			} else {
14688				|	push 0
14689			}
14690			|	mov r0, EX->run_time_cache
14691			|	add r0, opline->extended_value
14692			|	push r0
14693			|	PUSH_ZVAL_ADDR val_addr, r0
14694		|.endif
14695
14696		|	EXT_CALL zend_jit_assign_obj_helper, r0
14697
14698		|.if not(X64)
14699			|	add r4, 4
14700		|.endif
14701
14702		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14703			val_info |= MAY_BE_RC1|MAY_BE_RCN;
14704		}
14705
14706		|7:
14707		|	// FREE_OP_DATA();
14708		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline
14709		|	jmp >9
14710		|.code
14711	} else if (needs_val_dtor) {
14712		|.cold_code
14713		|7:
14714		|	// FREE_OP_DATA();
14715		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline
14716		|	jmp >9
14717		|.code
14718	}
14719
14720	|9:
14721	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
14722		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline
14723	}
14724
14725	if (may_throw) {
14726		if (!zend_jit_check_exception(Dst)) {
14727			return 0;
14728		}
14729	}
14730
14731	return 1;
14732}
14733
14734static int zend_jit_free(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, int may_throw)
14735{
14736	zend_jit_addr op1_addr = OP1_ADDR();
14737
14738	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
14739		if (may_throw) {
14740			|	SET_EX_OPLINE opline, r0
14741		}
14742		if (opline->opcode == ZEND_FE_FREE && (op1_info & (MAY_BE_OBJECT|MAY_BE_REF))) {
14743			if (op1_info & MAY_BE_ARRAY) {
14744				|	IF_ZVAL_TYPE op1_addr, IS_ARRAY, >7
14745			}
14746			|	mov FCARG1d, dword [FP + opline->op1.var + offsetof(zval, u2.fe_iter_idx)]
14747			|	cmp FCARG1d, -1
14748			|	je >7
14749			|	EXT_CALL zend_hash_iterator_del, r0
14750			|7:
14751		}
14752		|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline
14753		if (may_throw) {
14754			if (!zend_jit_check_exception(Dst)) {
14755				return 0;
14756			}
14757		}
14758	}
14759
14760	return 1;
14761}
14762
14763static int zend_jit_echo(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
14764{
14765	if (opline->op1_type == IS_CONST) {
14766		zval *zv;
14767		size_t len;
14768
14769		zv = RT_CONSTANT(opline, opline->op1);
14770		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
14771		len = Z_STRLEN_P(zv);
14772
14773		if (len > 0) {
14774			const char *str = Z_STRVAL_P(zv);
14775
14776			|	SET_EX_OPLINE opline, r0
14777			|.if X64
14778				|	LOAD_ADDR CARG1, str
14779				|	LOAD_ADDR CARG2, len
14780				|	EXT_CALL zend_write, r0
14781			|.else
14782				|	mov aword A2, len
14783				|	mov aword A1, str
14784				|	EXT_CALL zend_write, r0
14785			|.endif
14786			if (!zend_jit_check_exception(Dst)) {
14787				return 0;
14788			}
14789		}
14790	} else {
14791		zend_jit_addr op1_addr = OP1_ADDR();
14792
14793		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
14794
14795		|	SET_EX_OPLINE opline, r0
14796		|	GET_ZVAL_PTR r0, op1_addr
14797		|.if X64
14798		|	lea CARG1, aword [r0 + offsetof(zend_string, val)]
14799		|	mov CARG2, aword [r0 + offsetof(zend_string, len)]
14800		|	EXT_CALL zend_write, r0
14801		|.else
14802		|	add r0, offsetof(zend_string, val)
14803		|	mov aword A1, r0
14804		|	mov r0, aword [r0 + (offsetof(zend_string, len)-offsetof(zend_string, val))]
14805		|	mov aword A2, r0
14806		|	EXT_CALL zend_write, r0
14807		|.endif
14808		if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
14809			|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline
14810		}
14811		if (!zend_jit_check_exception(Dst)) {
14812			return 0;
14813		}
14814	}
14815	return 1;
14816}
14817
14818static 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)
14819{
14820	if (opline->op1_type == IS_CONST) {
14821		zval *zv;
14822		size_t len;
14823
14824		zv = RT_CONSTANT(opline, opline->op1);
14825		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
14826		len = Z_STRLEN_P(zv);
14827
14828		|	SET_ZVAL_LVAL res_addr, len
14829		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
14830			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14831		} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14832			return 0;
14833		}
14834	} else {
14835		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
14836
14837		if (Z_MODE(res_addr) == IS_REG) {
14838			|	GET_ZVAL_PTR Ra(Z_REG(res_addr)), op1_addr
14839			|	mov Ra(Z_REG(res_addr)), aword [Ra(Z_REG(res_addr))+offsetof(zend_string, len)]
14840			if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14841				return 0;
14842			}
14843		} else {
14844			|	GET_ZVAL_PTR r0, op1_addr
14845			|	mov r0, aword [r0 + offsetof(zend_string, len)]
14846			|	SET_ZVAL_LVAL res_addr, r0
14847			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14848		}
14849		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
14850	}
14851	return 1;
14852}
14853
14854static 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)
14855{
14856	if (opline->op1_type == IS_CONST) {
14857		zval *zv;
14858		zend_long count;
14859
14860		zv = RT_CONSTANT(opline, opline->op1);
14861		ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY);
14862		count = zend_hash_num_elements(Z_ARRVAL_P(zv));
14863
14864		|	SET_ZVAL_LVAL res_addr, count
14865		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
14866			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14867		} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14868			return 0;
14869		}
14870	} else {
14871		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY);
14872		// Note: See the implementation of ZEND_COUNT in Zend/zend_vm_def.h - arrays do not contain IS_UNDEF starting in php 8.1+.
14873
14874		if (Z_MODE(res_addr) == IS_REG) {
14875			|	GET_ZVAL_PTR Ra(Z_REG(res_addr)), op1_addr
14876			// Sign-extend the 32-bit value to a potentially 64-bit zend_long
14877			|	mov Rd(Z_REG(res_addr)), dword [Ra(Z_REG(res_addr))+offsetof(HashTable, nNumOfElements)]
14878			if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
14879				return 0;
14880			}
14881		} else {
14882			|	GET_ZVAL_PTR r0, op1_addr
14883			// Sign-extend the 32-bit value to a potentially 64-bit zend_long
14884			|	mov eax, dword [r0 + offsetof(HashTable, nNumOfElements)]
14885			|	SET_ZVAL_LVAL res_addr, r0
14886			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
14887		}
14888		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline
14889	}
14890
14891	if (may_throw) {
14892		return zend_jit_check_exception(Dst);
14893	}
14894	return 1;
14895}
14896
14897static int zend_jit_load_this(dasm_State **Dst, uint32_t var)
14898{
14899	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
14900
14901	|	mov FCARG1a, aword EX->This.value.ptr
14902	|	SET_ZVAL_PTR var_addr, FCARG1a
14903	|	SET_ZVAL_TYPE_INFO var_addr, IS_OBJECT_EX
14904	|	GC_ADDREF FCARG1a
14905
14906	return 1;
14907}
14908
14909static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool check_only)
14910{
14911	if (!op_array->scope || (op_array->fn_flags & ZEND_ACC_STATIC)) {
14912		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
14913			if (!JIT_G(current_frame) ||
14914			    !TRACE_FRAME_IS_THIS_CHECKED(JIT_G(current_frame))) {
14915
14916				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
14917				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14918
14919				if (!exit_addr) {
14920					return 0;
14921				}
14922
14923				|	cmp byte EX->This.u1.v.type, IS_OBJECT
14924				|	jne &exit_addr
14925
14926				if (JIT_G(current_frame)) {
14927					TRACE_FRAME_SET_THIS_CHECKED(JIT_G(current_frame));
14928				}
14929			}
14930		} else {
14931
14932			|	cmp byte EX->This.u1.v.type, IS_OBJECT
14933			|	jne >1
14934			|.cold_code
14935			|1:
14936			|	SET_EX_OPLINE opline, r0
14937			|	jmp ->invalid_this
14938			|.code
14939		}
14940	}
14941
14942	if (!check_only) {
14943		if (!zend_jit_load_this(Dst, opline->result.var)) {
14944			return 0;
14945		}
14946	}
14947
14948	return 1;
14949}
14950
14951static 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)
14952{
14953	uint32_t count;
14954	Bucket *p;
14955	const zend_op *target;
14956	int b;
14957	int32_t exit_point;
14958	const void *exit_addr;
14959
14960	|	test r0, r0
14961	if (default_label) {
14962		|	jz &default_label
14963	} else if (next_opline) {
14964		|	jz >3
14965	} else {
14966		|	jz =>default_b
14967	}
14968	|	LOAD_ADDR FCARG1a, jumptable
14969	|	sub r0, aword [FCARG1a + offsetof(HashTable, arData)]
14970	|	mov FCARG1a, (sizeof(Bucket) / sizeof(void*))
14971	|.if X64
14972	|	cqo
14973	|.else
14974	|	cdq
14975	|.endif
14976	|	idiv FCARG1a
14977	|.if X64
14978	if (!IS_32BIT(dasm_end)) {
14979		|	lea FCARG1a, aword [>4]
14980		|	jmp aword [FCARG1a + r0]
14981	} else {
14982		|	jmp aword [r0 + >4]
14983	}
14984	|.else
14985	|	jmp aword [r0 + >4]
14986	|.endif
14987	|.jmp_table
14988	|.align aword
14989	|4:
14990	if (trace_info) {
14991		trace_info->jmp_table_size += zend_hash_num_elements(jumptable);
14992	}
14993
14994	count = jumptable->nNumUsed;
14995	p = jumptable->arData;
14996	do {
14997		if (Z_TYPE(p->val) == IS_UNDEF) {
14998			if (default_label) {
14999				|	.aword &default_label
15000			} else if (next_opline) {
15001				|	.aword >3
15002			} else {
15003				|	.aword =>default_b
15004			}
15005		} else {
15006			target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val));
15007			if (!next_opline) {
15008				b = ssa->cfg.map[target - op_array->opcodes];
15009				|	.aword =>b
15010			} else if (next_opline == target) {
15011				|	.aword >3
15012			} else {
15013				exit_point = zend_jit_trace_get_exit_point(target, 0);
15014				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15015				if (!exit_addr) {
15016					return 0;
15017				}
15018				|	.aword &exit_addr
15019			}
15020		}
15021		p++;
15022		count--;
15023	} while (count);
15024	|.code
15025
15026	return 1;
15027}
15028
15029static 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)
15030{
15031	HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
15032	const zend_op *next_opline = NULL;
15033
15034	if (trace) {
15035		ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
15036		ZEND_ASSERT(trace->opline != NULL);
15037		next_opline = trace->opline;
15038	}
15039
15040	if (opline->op1_type == IS_CONST) {
15041		zval *zv = RT_CONSTANT(opline, opline->op1);
15042		zval *jump_zv = NULL;
15043		int b;
15044
15045		if (opline->opcode == ZEND_SWITCH_LONG) {
15046			if (Z_TYPE_P(zv) == IS_LONG) {
15047				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
15048			}
15049		} else if (opline->opcode == ZEND_SWITCH_STRING) {
15050			if (Z_TYPE_P(zv) == IS_STRING) {
15051				jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv));
15052			}
15053		} else if (opline->opcode == ZEND_MATCH) {
15054			if (Z_TYPE_P(zv) == IS_LONG) {
15055				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
15056			} else if (Z_TYPE_P(zv) == IS_STRING) {
15057				jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv));
15058			}
15059		} else {
15060			ZEND_UNREACHABLE();
15061		}
15062		if (next_opline) {
15063			const zend_op *target;
15064
15065			if (jump_zv != NULL) {
15066				target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
15067			} else {
15068				target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
15069			}
15070			ZEND_ASSERT(target == next_opline);
15071		} else {
15072			if (jump_zv != NULL) {
15073				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes];
15074			} else {
15075				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
15076			}
15077			|	jmp =>b
15078		}
15079	} else {
15080		zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
15081		uint32_t op1_info = OP1_INFO();
15082		zend_jit_addr op1_addr = OP1_ADDR();
15083		const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
15084		const zend_op *target;
15085		int default_b = next_opline ? -1 : ssa->cfg.map[default_opline - op_array->opcodes];
15086		int b;
15087		int32_t exit_point;
15088		const void *fallback_label = NULL;
15089		const void *default_label = NULL;
15090		const void *exit_addr;
15091
15092		if (next_opline) {
15093			if (next_opline != opline + 1) {
15094				exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
15095				fallback_label = zend_jit_trace_get_exit_addr(exit_point);
15096				if (!fallback_label) {
15097					return 0;
15098				}
15099			}
15100			if (next_opline != default_opline) {
15101				exit_point = zend_jit_trace_get_exit_point(default_opline, 0);
15102				default_label = zend_jit_trace_get_exit_addr(exit_point);
15103				if (!default_label) {
15104					return 0;
15105				}
15106			}
15107		}
15108
15109		if (opline->opcode == ZEND_SWITCH_LONG) {
15110			if (op1_info & MAY_BE_LONG) {
15111				if (op1_info & MAY_BE_REF) {
15112					|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >1
15113					|	GET_ZVAL_LVAL ZREG_FCARG2, 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_LONG, &fallback_label
15125					} else {
15126						|	IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_LONG, >3
15127					}
15128					|	mov FCARG2a, aword [FCARG2a + offsetof(zend_reference, val.value.lval)]
15129					|	jmp >2
15130					|.code
15131					|2:
15132				} else {
15133					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
15134						if (fallback_label) {
15135							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &fallback_label
15136						} else {
15137							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
15138						}
15139					}
15140					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr
15141				}
15142				if (HT_IS_PACKED(jumptable)) {
15143					uint32_t count = jumptable->nNumUsed;
15144					Bucket *p = jumptable->arData;
15145
15146					|	cmp FCARG2a, jumptable->nNumUsed
15147					if (default_label) {
15148						|	jae &default_label
15149					} else if (next_opline) {
15150						|	jae >3
15151					} else {
15152						|	jae =>default_b
15153					}
15154					|.if X64
15155						if (!IS_32BIT(dasm_end)) {
15156							|	lea r0, aword [>4]
15157							|	jmp aword [r0 + FCARG2a * 8]
15158						} else {
15159							|	jmp aword [FCARG2a * 8 + >4]
15160						}
15161					|.else
15162					|	jmp aword [FCARG2a * 4 + >4]
15163					|.endif
15164					|.jmp_table
15165					|.align aword
15166					|4:
15167					if (trace_info) {
15168						trace_info->jmp_table_size += count;
15169					}
15170					p = jumptable->arData;
15171					do {
15172						if (Z_TYPE(p->val) == IS_UNDEF) {
15173							if (default_label) {
15174								|	.aword &default_label
15175							} else if (next_opline) {
15176								|	.aword >3
15177							} else {
15178								|	.aword =>default_b
15179							}
15180						} else {
15181							target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val));
15182							if (!next_opline) {
15183								b = ssa->cfg.map[target - op_array->opcodes];
15184								|	.aword =>b
15185							} else if (next_opline == target) {
15186								|	.aword >3
15187							} else {
15188								exit_point = zend_jit_trace_get_exit_point(target, 0);
15189								exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15190								if (!exit_addr) {
15191									return 0;
15192								}
15193								|	.aword &exit_addr
15194							}
15195						}
15196						p++;
15197						count--;
15198					} while (count);
15199					|.code
15200					|3:
15201				} else {
15202					|	LOAD_ADDR FCARG1a, jumptable
15203					|	EXT_CALL zend_hash_index_find, r0
15204					if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
15205						return 0;
15206					}
15207					|3:
15208				}
15209			}
15210		} else if (opline->opcode == ZEND_SWITCH_STRING) {
15211			if (op1_info & MAY_BE_STRING) {
15212				if (op1_info & MAY_BE_REF) {
15213					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >1
15214					|	GET_ZVAL_PTR FCARG2a, op1_addr
15215					|.cold_code
15216					|1:
15217					|	// ZVAL_DEREF(op)
15218					if (fallback_label) {
15219						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label
15220					} else {
15221						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3
15222					}
15223					|	GET_ZVAL_PTR FCARG2a, op1_addr
15224					if (fallback_label) {
15225						|	IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_STRING, &fallback_label
15226					} else {
15227						|	IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_STRING, >3
15228					}
15229					|	mov FCARG2a, aword [FCARG2a + offsetof(zend_reference, val.value.ptr)]
15230					|	jmp >2
15231					|.code
15232					|2:
15233				} else {
15234					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_STRING)) {
15235						if (fallback_label) {
15236							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &fallback_label
15237						} else {
15238							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3
15239						}
15240					}
15241					|	GET_ZVAL_PTR FCARG2a, op1_addr
15242				}
15243				|	LOAD_ADDR FCARG1a, jumptable
15244				|	EXT_CALL zend_hash_find, r0
15245				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
15246					return 0;
15247				}
15248				|3:
15249			}
15250		} else if (opline->opcode == ZEND_MATCH) {
15251			if (op1_info & (MAY_BE_LONG|MAY_BE_STRING)) {
15252				if (op1_info & MAY_BE_REF) {
15253					|	LOAD_ZVAL_ADDR FCARG2a, op1_addr
15254					|	ZVAL_DEREF FCARG2a, op1_info
15255					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
15256				}
15257				|	LOAD_ADDR FCARG1a, jumptable
15258				if (op1_info & MAY_BE_LONG) {
15259					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
15260						if (op1_info & MAY_BE_STRING) {
15261							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >5
15262						} else if (op1_info & MAY_BE_UNDEF) {
15263							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6
15264						} else if (default_label) {
15265							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &default_label
15266						} else if (next_opline) {
15267							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
15268						} else {
15269							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, =>default_b
15270						}
15271					}
15272					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr
15273					|	EXT_CALL zend_hash_index_find, r0
15274					if (op1_info & MAY_BE_STRING) {
15275						|	jmp >2
15276					}
15277				}
15278				if (op1_info & MAY_BE_STRING) {
15279					|5:
15280					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_STRING))) {
15281						if (op1_info & MAY_BE_UNDEF) {
15282							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6
15283						} else if (default_label) {
15284							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &default_label
15285						} else if (next_opline) {
15286							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3
15287						} else {
15288							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, =>default_b
15289						}
15290					}
15291					|	GET_ZVAL_PTR FCARG2a, op1_addr
15292					|	EXT_CALL zend_hash_find, r0
15293				}
15294				|2:
15295				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
15296					return 0;
15297				}
15298			}
15299			if (op1_info & MAY_BE_UNDEF) {
15300				|6:
15301				if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_STRING))) {
15302					if (default_label) {
15303						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, &default_label
15304					} else if (next_opline) {
15305						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >3
15306					} else {
15307						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, =>default_b
15308					}
15309				}
15310				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
15311				|	SET_EX_OPLINE opline, r0
15312				|	mov FCARG1d, opline->op1.var
15313				|	EXT_CALL zend_jit_undefined_op_helper, r0
15314				if (!zend_jit_check_exception_undef_result(Dst, opline)) {
15315					return 0;
15316				}
15317			}
15318			if (default_label) {
15319				|	jmp &default_label
15320			} else if (next_opline) {
15321				|	jmp >3
15322			} else {
15323				|	jmp =>default_b
15324			}
15325			|3:
15326		} else {
15327			ZEND_UNREACHABLE();
15328		}
15329	}
15330	return 1;
15331}
15332
15333static bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info)
15334{
15335	zend_arg_info *arg_info = &op_array->arg_info[-1];
15336	ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
15337	zend_jit_addr op1_addr = OP1_ADDR();
15338	bool needs_slow_check = 1;
15339	bool slow_check_in_cold = 1;
15340	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
15341
15342	if (type_mask == 0) {
15343		slow_check_in_cold = 0;
15344	} else {
15345		if (((op1_info & MAY_BE_ANY) & type_mask) == 0) {
15346			slow_check_in_cold = 0;
15347		} else if (((op1_info & MAY_BE_ANY) | type_mask) == type_mask) {
15348			needs_slow_check = 0;
15349		} else if (is_power_of_two(type_mask)) {
15350			uint32_t type_code = concrete_type(type_mask);
15351			|	IF_NOT_ZVAL_TYPE op1_addr, type_code, >6
15352		} else {
15353			|	mov edx, 1
15354			|	GET_ZVAL_TYPE cl, op1_addr
15355			|	shl edx, cl
15356			|	test edx, type_mask
15357			|	je >6
15358		}
15359	}
15360	if (needs_slow_check) {
15361		if (slow_check_in_cold) {
15362			|.cold_code
15363			|6:
15364		}
15365		|	SET_EX_OPLINE opline, r1
15366		if (op1_info & MAY_BE_UNDEF) {
15367			|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >7
15368			|	mov FCARG1a, opline->op1.var
15369			|	EXT_CALL zend_jit_undefined_op_helper, FCARG2a
15370			|	test r0, r0
15371			|	jz ->exception_handler
15372			|	LOAD_ADDR_ZTS FCARG1a, executor_globals, uninitialized_zval
15373			|	jmp >8
15374		}
15375		|7:
15376		|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
15377		|8:
15378		|	mov FCARG2a, EX->func
15379		|.if X64
15380			|	LOAD_ADDR CARG3, (ptrdiff_t)arg_info
15381			|	mov r0, EX->run_time_cache
15382			|	lea CARG4, aword [r0+opline->op2.num]
15383			|	EXT_CALL zend_jit_verify_return_slow, r0
15384		|.else
15385			|	sub r4, 8
15386			|	mov r0, EX->run_time_cache
15387			|	add r0, opline->op2.num
15388			|	push r0
15389			|	push (ptrdiff_t)arg_info
15390			|	EXT_CALL zend_jit_verify_return_slow, r0
15391			|	add r4, 8
15392		|.endif
15393		if (!zend_jit_check_exception(Dst)) {
15394			return 0;
15395		}
15396		if (slow_check_in_cold) {
15397			|	jmp >9
15398			|.code
15399		}
15400	}
15401	|9:
15402	return 1;
15403}
15404
15405static int zend_jit_isset_isempty_cv(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr,  zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
15406{
15407	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
15408
15409	// TODO: support for empty() ???
15410	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
15411
15412	if (op1_info & MAY_BE_REF) {
15413		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
15414			|	LOAD_ZVAL_ADDR FCARG1a, op1_addr
15415			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
15416		}
15417		|	ZVAL_DEREF FCARG1a, op1_info
15418		|1:
15419	}
15420
15421	if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) {
15422		if (exit_addr) {
15423			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPZ);
15424		} else if (smart_branch_opcode) {
15425			if (smart_branch_opcode == ZEND_JMPNZ) {
15426				|	jmp =>target_label
15427			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
15428				|	jmp =>target_label2
15429			}
15430		} else {
15431			|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE
15432		}
15433	} else if (!(op1_info & (MAY_BE_ANY - MAY_BE_NULL))) {
15434		if (exit_addr) {
15435			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPNZ);
15436		} else if (smart_branch_opcode) {
15437			if (smart_branch_opcode != ZEND_JMPNZ) {
15438				|	jmp =>target_label
15439			}
15440		} else {
15441			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE
15442		}
15443	} else {
15444		ZEND_ASSERT(Z_MODE(op1_addr) == IS_MEM_ZVAL);
15445		|	cmp byte [Ra(Z_REG(op1_addr))+Z_OFFSET(op1_addr)+offsetof(zval, u1.v.type)], IS_NULL
15446		if (exit_addr) {
15447			if (smart_branch_opcode == ZEND_JMPNZ) {
15448				|	jg &exit_addr
15449			} else {
15450				|	jle &exit_addr
15451			}
15452		} else if (smart_branch_opcode) {
15453			if (smart_branch_opcode == ZEND_JMPZ) {
15454				|	jle =>target_label
15455			} else if (smart_branch_opcode == ZEND_JMPNZ) {
15456				|	jg =>target_label
15457			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
15458				|	jle =>target_label
15459				|	jmp =>target_label2
15460			} else {
15461				ZEND_UNREACHABLE();
15462			}
15463		} else {
15464			|	setg al
15465			|	movzx eax, al
15466			|	lea eax, [eax + IS_FALSE]
15467			|	SET_ZVAL_TYPE_INFO res_addr, eax
15468		}
15469	}
15470
15471	return 1;
15472}
15473
15474static int zend_jit_fe_reset(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
15475{
15476	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
15477
15478	if (opline->op1_type == IS_CONST) {
15479		zval *zv = RT_CONSTANT(opline, opline->op1);
15480
15481		|	ZVAL_COPY_CONST res_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_R0
15482		if (Z_REFCOUNTED_P(zv)) {
15483			|	ADDREF_CONST zv, r0
15484		}
15485	} else {
15486		zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
15487
15488		|	// ZVAL_COPY(res, value);
15489		|	ZVAL_COPY_VALUE res_addr, -1, op1_addr, op1_info, ZREG_R0, ZREG_FCARG1
15490		if (opline->op1_type == IS_CV) {
15491			|	TRY_ADDREF op1_info, ah, FCARG1a
15492		}
15493	}
15494	|	// Z_FE_POS_P(res) = 0;
15495	|	mov dword [FP + opline->result.var + offsetof(zval, u2.fe_pos)], 0
15496
15497	return 1;
15498}
15499
15500static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, uint32_t op2_info, unsigned int target_label, zend_uchar exit_opcode, const void *exit_addr)
15501{
15502	zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
15503
15504	if (!MAY_BE_HASH(op1_info) && !MAY_BE_PACKED(op1_info)) {
15505		/* empty array */
15506		if (exit_addr) {
15507			if (exit_opcode == ZEND_JMP) {
15508				|	jmp &exit_addr
15509			}
15510		} else {
15511			|	jmp =>target_label
15512		}
15513		return 1;
15514	}
15515
15516	|	// array = EX_VAR(opline->op1.var);
15517	|	// fe_ht = Z_ARRVAL_P(array);
15518	|	GET_ZVAL_PTR FCARG1a, op1_addr
15519	|	// pos = Z_FE_POS_P(array);
15520	|	mov eax, dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)]
15521	|	// p = fe_ht->arData + pos;
15522	|.if X64
15523		||	ZEND_ASSERT(sizeof(Bucket) == 32);
15524		|	mov FCARG2d, eax
15525		|	shl FCARG2a, 5
15526	|.else
15527		|	imul FCARG2a, r0, sizeof(Bucket)
15528	|.endif
15529	|	add FCARG2a, aword [FCARG1a + offsetof(zend_array, arData)]
15530	|1:
15531	|	// if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
15532	|	cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], eax
15533	|	// ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
15534	|   // ZEND_VM_CONTINUE();
15535	if (exit_addr) {
15536		if (exit_opcode == ZEND_JMP) {
15537			|	jbe &exit_addr
15538		} else {
15539			|	jbe >3
15540		}
15541	} else {
15542		|	jbe =>target_label
15543	}
15544	|	// pos++;
15545	|	add eax, 1
15546	|	// value_type = Z_TYPE_INFO_P(value);
15547	|	// if (EXPECTED(value_type != IS_UNDEF)) {
15548	if (!exit_addr || exit_opcode == ZEND_JMP) {
15549		|	IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, >3
15550	} else {
15551		|	IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, &exit_addr
15552	}
15553	|	// p++;
15554	|	add FCARG2a, sizeof(Bucket)
15555	|	jmp <1
15556	|3:
15557
15558	if (!exit_addr || exit_opcode == ZEND_JMP) {
15559		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
15560		zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
15561		uint32_t val_info;
15562
15563		|	// Z_FE_POS_P(array) = pos + 1;
15564		|	mov dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)], eax
15565
15566		if (RETURN_VALUE_USED(opline)) {
15567			zend_jit_addr res_addr = RES_ADDR();
15568
15569			if ((op1_info & MAY_BE_ARRAY_KEY_LONG)
15570			 && (op1_info & MAY_BE_ARRAY_KEY_STRING)) {
15571				|	// if (!p->key) {
15572				|	cmp aword [FCARG2a + offsetof(Bucket, key)], 0
15573				|	jz >2
15574			}
15575			if (op1_info & MAY_BE_ARRAY_KEY_STRING) {
15576				|	// ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key);
15577				|	mov r0, aword [FCARG2a + offsetof(Bucket, key)]
15578				|	SET_ZVAL_PTR res_addr, r0
15579				|	test dword [r0 + offsetof(zend_refcounted, gc.u.type_info)], IS_STR_INTERNED
15580				|	jz >1
15581				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING
15582				|	jmp >3
15583				|1:
15584				|	GC_ADDREF r0
15585				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX
15586
15587				if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
15588				    |	jmp >3
15589					|2:
15590				}
15591			}
15592			if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
15593				|	// ZVAL_LONG(EX_VAR(opline->result.var), p->h);
15594				|	mov r0, aword [FCARG2a + offsetof(Bucket, h)]
15595				|	SET_ZVAL_LVAL res_addr, r0
15596				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG
15597			}
15598			|3:
15599		}
15600
15601		val_info = ((op1_info & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
15602		if (val_info & MAY_BE_ARRAY) {
15603			val_info |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
15604		}
15605		if (op1_info & MAY_BE_ARRAY_OF_REF) {
15606			val_info |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY |
15607				MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
15608		} else if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
15609			val_info |= MAY_BE_RC1 | MAY_BE_RCN;
15610		}
15611
15612		if (opline->op2_type == IS_CV) {
15613			|	// zend_assign_to_variable(variable_ptr, value, IS_CV, EX_USES_STRICT_TYPES());
15614			if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, op2_info, -1, IS_CV, val_addr, val_info, 0, 1)) {
15615				return 0;
15616			}
15617		} else {
15618			|	// ZVAL_COPY(res, value);
15619			|	ZVAL_COPY_VALUE var_addr, -1, val_addr, val_info, ZREG_R0, ZREG_FCARG1
15620			|	TRY_ADDREF val_info, ah, FCARG1a
15621		}
15622	}
15623
15624	return 1;
15625}
15626
15627static int zend_jit_fetch_constant(dasm_State          **Dst,
15628                                   const zend_op        *opline,
15629                                   const zend_op_array  *op_array,
15630                                   zend_ssa             *ssa,
15631                                   const zend_ssa_op    *ssa_op,
15632                                   zend_jit_addr         res_addr)
15633{
15634	zval *zv = RT_CONSTANT(opline, opline->op2) + 1;
15635	zend_jit_addr const_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
15636	uint32_t res_info = RES_INFO();
15637
15638	|	// c = CACHED_PTR(opline->extended_value);
15639	|	mov FCARG1a, EX->run_time_cache
15640	|	mov r0, aword [FCARG1a + opline->extended_value]
15641	|	// if (c != NULL)
15642	|	test r0, r0
15643	|	jz >9
15644	if (!zend_jit_is_persistent_constant(zv, opline->op1.num)) {
15645		|	// if (!IS_SPECIAL_CACHE_VAL(c))
15646		|	test r0, CACHE_SPECIAL
15647		|	jnz >9
15648	}
15649	|8:
15650
15651	if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) {
15652		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
15653		uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
15654		int32_t exit_point;
15655		const void *exit_addr = NULL;
15656
15657		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
15658		SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
15659		exit_point = zend_jit_trace_get_exit_point(opline+1, 0);
15660		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
15661		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15662		if (!exit_addr) {
15663			return 0;
15664		}
15665		res_info &= ~MAY_BE_GUARD;
15666		ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
15667
15668		zend_uchar type = concrete_type(res_info);
15669
15670		if (type < IS_STRING) {
15671			|	IF_NOT_ZVAL_TYPE const_addr, type, &exit_addr
15672		} else {
15673			|	GET_ZVAL_TYPE_INFO edx, const_addr
15674			|	IF_NOT_TYPE dl, type, &exit_addr
15675		}
15676		|	ZVAL_COPY_VALUE_V res_addr, -1, const_addr, res_info, ZREG_R0, ZREG_R1
15677		if (type < IS_STRING) {
15678			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
15679				|	SET_ZVAL_TYPE_INFO res_addr, type
15680			} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
15681				return 0;
15682			}
15683		} else {
15684			|	SET_ZVAL_TYPE_INFO res_addr, edx
15685			|	TRY_ADDREF res_info, dh, r1
15686		}
15687	} else {
15688		|	// ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value); (no dup)
15689		|	ZVAL_COPY_VALUE res_addr, MAY_BE_ANY, const_addr, MAY_BE_ANY, ZREG_R0, ZREG_R1
15690		|	TRY_ADDREF MAY_BE_ANY, ah, r1
15691	}
15692
15693	|.cold_code
15694	|9:
15695	|	// SAVE_OPLINE();
15696	|	SET_EX_OPLINE opline, r0
15697	|	// zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num OPLINE_CC EXECUTE_DATA_CC);
15698	|	LOAD_ADDR FCARG1a, zv
15699	|	mov FCARG2a, opline->op1.num
15700	|	EXT_CALL zend_jit_get_constant, r0
15701	|	// ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
15702	|	test r0, r0
15703	|	jnz <8
15704	|	jmp ->exception_handler
15705	|.code
15706
15707	return 1;
15708}
15709
15710static int zend_jit_in_array(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr,  zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
15711{
15712	HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
15713	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
15714
15715	ZEND_ASSERT(opline->op1_type != IS_VAR && opline->op1_type != IS_TMP_VAR);
15716	ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_STRING);
15717
15718	|	// result = zend_hash_find_ex(ht, Z_STR_P(op1), OP1_TYPE == IS_CONST);
15719	|	LOAD_ADDR FCARG1a, ht
15720	if (opline->op1_type != IS_CONST) {
15721		|	GET_ZVAL_PTR FCARG2a, op1_addr
15722		|	EXT_CALL zend_hash_find, r0
15723	} else {
15724		zend_string *str = Z_STR_P(RT_CONSTANT(opline, opline->op1));
15725		|	LOAD_ADDR FCARG2a, str
15726		|	EXT_CALL zend_hash_find_known_hash, r0
15727	}
15728	|	test r0, r0
15729	if (exit_addr) {
15730		if (smart_branch_opcode == ZEND_JMPZ) {
15731			|	jz &exit_addr
15732		} else {
15733			|	jnz &exit_addr
15734		}
15735	} else if (smart_branch_opcode) {
15736		if (smart_branch_opcode == ZEND_JMPZ) {
15737			|	jz =>target_label
15738		} else if (smart_branch_opcode == ZEND_JMPNZ) {
15739			|	jnz =>target_label
15740		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
15741			|	jz =>target_label
15742			|	jmp =>target_label2
15743		} else {
15744			ZEND_UNREACHABLE();
15745		}
15746	} else {
15747		|	setnz al
15748		|	movzx eax, al
15749		|	lea eax, [eax + IS_FALSE]
15750		|	SET_ZVAL_TYPE_INFO res_addr, eax
15751	}
15752
15753	return 1;
15754}
15755
15756static int zend_jit_rope(dasm_State **Dst, const zend_op *opline, uint32_t op2_info)
15757{
15758	uint32_t offset;
15759
15760	offset = (opline->opcode == ZEND_ROPE_INIT) ?
15761		opline->result.var :
15762		opline->op1.var + opline->extended_value * sizeof(zend_string*);
15763
15764	if (opline->op2_type == IS_CONST) {
15765		zval *zv = RT_CONSTANT(opline, opline->op2);
15766		zend_string *str;
15767
15768		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
15769		str = Z_STR_P(zv);
15770		|	ADDR_STORE aword [FP + offset], str, r0
15771	} else {
15772		zend_jit_addr op2_addr = OP2_ADDR();
15773
15774		ZEND_ASSERT((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
15775
15776		|	GET_ZVAL_PTR r1, op2_addr
15777		|	mov aword [FP + offset], r1
15778		if (opline->op2_type == IS_CV) {
15779			|	GET_ZVAL_TYPE_INFO eax, op2_addr
15780			|	TRY_ADDREF op2_info, ah, r1
15781		}
15782	}
15783
15784	if (opline->opcode == ZEND_ROPE_END) {
15785		zend_jit_addr res_addr = RES_ADDR();
15786
15787		|	lea FCARG1a, [FP + opline->op1.var]
15788		|	mov FCARG2d, opline->extended_value
15789		|	EXT_CALL zend_jit_rope_end, r0
15790		|	SET_ZVAL_PTR res_addr, r0
15791		|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX
15792	}
15793
15794	return 1;
15795}
15796
15797static bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_addr)
15798{
15799	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
15800	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15801
15802	if (!exit_addr) {
15803		return 0;
15804	}
15805	|	IF_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr
15806
15807	return 1;
15808}
15809
15810static 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)
15811{
15812	zend_jit_addr var_addr = *var_addr_ptr;
15813	uint32_t var_info = *var_info_ptr;
15814	const void *exit_addr = NULL;
15815
15816	if (add_ref_guard || add_type_guard) {
15817		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
15818
15819		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15820		if (!exit_addr) {
15821			return 0;
15822		}
15823	}
15824
15825	if (add_ref_guard) {
15826		|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr
15827	}
15828	if (opline->opcode == ZEND_INIT_METHOD_CALL && opline->op1_type == IS_VAR) {
15829		/* Hack: Convert reference to regular value to simplify JIT code for INIT_METHOD_CALL */
15830		if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
15831			|	LOAD_ZVAL_ADDR FCARG1a, var_addr
15832		}
15833		|	EXT_CALL zend_jit_unref_helper, r0
15834	} else {
15835		|	GET_ZVAL_PTR FCARG1a, var_addr
15836		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, offsetof(zend_reference, val));
15837		*var_addr_ptr = var_addr;
15838	}
15839
15840	if (var_type != IS_UNKNOWN) {
15841		var_type &= ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED);
15842	}
15843	if (add_type_guard
15844	 && var_type != IS_UNKNOWN
15845	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
15846		|	IF_NOT_ZVAL_TYPE var_addr, var_type, &exit_addr
15847
15848		ZEND_ASSERT(var_info & (1 << var_type));
15849		if (var_type < IS_STRING) {
15850			var_info = (1 << var_type);
15851		} else if (var_type != IS_ARRAY) {
15852			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
15853		} else {
15854			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));
15855		}
15856
15857		*var_info_ptr = var_info;
15858	} else {
15859		var_info &= ~MAY_BE_REF;
15860		*var_info_ptr = var_info;
15861	}
15862	*var_info_ptr |= MAY_BE_GUARD; /* prevent generation of specialized zval dtor */
15863
15864	return 1;
15865}
15866
15867static 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)
15868{
15869	zend_jit_addr var_addr = *var_addr_ptr;
15870	uint32_t var_info = *var_info_ptr;
15871	int32_t exit_point;
15872	const void *exit_addr;
15873
15874	if (add_indirect_guard) {
15875		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
15876		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15877
15878		if (!exit_addr) {
15879			return 0;
15880		}
15881		|	IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr
15882		|	GET_ZVAL_PTR FCARG1a, var_addr
15883	} else {
15884		/* May be already loaded into FCARG1a or RAX by previus FETCH_OBJ_W/DIM_W */
15885		if (opline->op1_type != IS_VAR ||
15886				(opline-1)->result_type != IS_VAR  ||
15887				(opline-1)->result.var != opline->op1.var ||
15888				(opline-1)->op1_type == IS_VAR ||
15889				(opline-1)->op2_type == IS_VAR ||
15890				(opline-1)->op2_type == IS_TMP_VAR) {
15891			|	GET_ZVAL_PTR FCARG1a, var_addr
15892		} else if ((opline-1)->opcode == ZEND_FETCH_DIM_W || (opline-1)->opcode == ZEND_FETCH_DIM_RW) {
15893			|	mov FCARG1a, r0
15894		}
15895	}
15896	*var_info_ptr &= ~MAY_BE_INDIRECT;
15897	var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
15898	*var_addr_ptr = var_addr;
15899
15900	if (var_type != IS_UNKNOWN) {
15901		var_type &= ~(IS_TRACE_INDIRECT|IS_TRACE_PACKED);
15902	}
15903	if (!(var_type & IS_TRACE_REFERENCE)
15904	 && var_type != IS_UNKNOWN
15905	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
15906		exit_point = zend_jit_trace_get_exit_point(opline, 0);
15907		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
15908
15909		if (!exit_addr) {
15910			return 0;
15911		}
15912
15913		|	IF_NOT_Z_TYPE FCARG1a, var_type, &exit_addr
15914
15915		//var_info = zend_jit_trace_type_to_info_ex(var_type, var_info);
15916		ZEND_ASSERT(var_info & (1 << var_type));
15917		if (var_type < IS_STRING) {
15918			var_info = (1 << var_type);
15919		} else if (var_type != IS_ARRAY) {
15920			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
15921		} else {
15922			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));
15923		}
15924
15925		*var_info_ptr = var_info;
15926	}
15927
15928	return 1;
15929}
15930
15931static 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)
15932{
15933	if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) {
15934		return 0;
15935	}
15936
15937	switch (opline->opcode) {
15938		case ZEND_QM_ASSIGN:
15939		case ZEND_SEND_VAR:
15940		case ZEND_ASSIGN:
15941		case ZEND_PRE_INC:
15942		case ZEND_PRE_DEC:
15943		case ZEND_POST_INC:
15944		case ZEND_POST_DEC:
15945			return 1;
15946		case ZEND_ADD:
15947		case ZEND_SUB:
15948		case ZEND_MUL:
15949		case ZEND_BW_OR:
15950		case ZEND_BW_AND:
15951		case ZEND_BW_XOR:
15952			if (def_var == ssa_op->result_def &&
15953			    use_var == ssa_op->op1_use) {
15954				return 1;
15955			}
15956			break;
15957		default:
15958			break;
15959	}
15960	return 0;
15961}
15962
15963static 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)
15964{
15965	uint32_t op1_info, op2_info;
15966
15967	switch (opline->opcode) {
15968		case ZEND_SEND_VAR:
15969		case ZEND_SEND_VAL:
15970		case ZEND_SEND_VAL_EX:
15971			return (opline->op2_type != IS_CONST);
15972		case ZEND_QM_ASSIGN:
15973		case ZEND_IS_SMALLER:
15974		case ZEND_IS_SMALLER_OR_EQUAL:
15975		case ZEND_IS_EQUAL:
15976		case ZEND_IS_NOT_EQUAL:
15977		case ZEND_IS_IDENTICAL:
15978		case ZEND_IS_NOT_IDENTICAL:
15979		case ZEND_CASE:
15980			return 1;
15981		case ZEND_RETURN:
15982			return (op_array->type != ZEND_EVAL_CODE && op_array->function_name);
15983		case ZEND_ASSIGN:
15984			op1_info = OP1_INFO();
15985			op2_info = OP2_INFO();
15986			return
15987				opline->op1_type == IS_CV &&
15988				!(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_RESOURCE|MAY_BE_OBJECT|MAY_BE_REF)) &&
15989				!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)));
15990		case ZEND_ADD:
15991		case ZEND_SUB:
15992		case ZEND_MUL:
15993			op1_info = OP1_INFO();
15994			op2_info = OP2_INFO();
15995			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE)));
15996		case ZEND_BW_OR:
15997		case ZEND_BW_AND:
15998		case ZEND_BW_XOR:
15999		case ZEND_SL:
16000		case ZEND_SR:
16001		case ZEND_MOD:
16002			op1_info = OP1_INFO();
16003			op2_info = OP2_INFO();
16004			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG));
16005		case ZEND_PRE_INC:
16006		case ZEND_PRE_DEC:
16007		case ZEND_POST_INC:
16008		case ZEND_POST_DEC:
16009			op1_info = OP1_INFO();
16010			op2_info = OP1_DEF_INFO();
16011			return opline->op1_type == IS_CV
16012				&& !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG))
16013				&& (op2_info & MAY_BE_LONG);
16014		case ZEND_STRLEN:
16015			op1_info = OP1_INFO();
16016			return (opline->op1_type & (IS_CV|IS_CONST))
16017				&& (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_STRING;
16018		case ZEND_COUNT:
16019			op1_info = OP1_INFO();
16020			return (opline->op1_type & (IS_CV|IS_CONST))
16021				&& (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_ARRAY;
16022		case ZEND_JMPZ:
16023		case ZEND_JMPNZ:
16024			if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
16025				if (!ssa->cfg.map) {
16026					return 0;
16027				}
16028				if (opline > op_array->opcodes + ssa->cfg.blocks[ssa->cfg.map[opline-op_array->opcodes]].start &&
16029				    ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
16030					return 0;
16031				}
16032			}
16033			ZEND_FALLTHROUGH;
16034		case ZEND_BOOL:
16035		case ZEND_BOOL_NOT:
16036		case ZEND_JMPZNZ:
16037		case ZEND_JMPZ_EX:
16038		case ZEND_JMPNZ_EX:
16039			return 1;
16040		case ZEND_FETCH_CONSTANT:
16041			return 1;
16042		case ZEND_FETCH_DIM_R:
16043			op1_info = OP1_INFO();
16044			op2_info = OP2_INFO();
16045			if (trace
16046			 && trace->op1_type != IS_UNKNOWN
16047			 && (trace->op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)) == IS_ARRAY) {
16048				op1_info &= ~((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY);
16049			}
16050			return ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) &&
16051				(!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || !(op1_info & MAY_BE_RC1)) &&
16052					(((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) ||
16053					 (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) &&
16054						 (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & MAY_BE_RC1))));
16055	}
16056	return 0;
16057}
16058
16059static bool zend_jit_var_supports_reg(zend_ssa *ssa, int var)
16060{
16061	if (ssa->vars[var].no_val) {
16062		/* we don't need the value */
16063		return 0;
16064	}
16065
16066	if (!(JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL)) {
16067		/* Disable global register allocation,
16068		 * register allocation for SSA variables connected through Phi functions
16069		 */
16070		if (ssa->vars[var].definition_phi) {
16071			return 0;
16072		}
16073		if (ssa->vars[var].phi_use_chain) {
16074			zend_ssa_phi *phi = ssa->vars[var].phi_use_chain;
16075			do {
16076				if (!ssa->vars[phi->ssa_var].no_val) {
16077					return 0;
16078				}
16079				phi = zend_ssa_next_use_phi(ssa, var, phi);
16080			} while (phi);
16081		}
16082	}
16083
16084	if (((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_DOUBLE) &&
16085	    ((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_LONG)) {
16086	    /* bad type */
16087		return 0;
16088	}
16089
16090	return 1;
16091}
16092
16093static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, int var)
16094{
16095	if (!zend_jit_var_supports_reg(ssa, var)) {
16096		return 0;
16097	}
16098
16099	if (ssa->vars[var].definition >= 0) {
16100		uint32_t def = ssa->vars[var].definition;
16101		if (!zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + def, ssa->ops + def, NULL)) {
16102			return 0;
16103		}
16104	}
16105
16106	if (ssa->vars[var].use_chain >= 0) {
16107		int use = ssa->vars[var].use_chain;
16108
16109		do {
16110			if (!zend_ssa_is_no_val_use(op_array->opcodes + use, ssa->ops + use, var) &&
16111			    !zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + use, ssa->ops + use, NULL)) {
16112				return 0;
16113			}
16114			use = zend_ssa_next_use(ssa->ops, var, use);
16115		} while (use >= 0);
16116	}
16117
16118	return 1;
16119}
16120
16121static bool zend_needs_extra_reg_for_const(const zend_op *opline, zend_uchar op_type, znode_op op)
16122{
16123|.if X64
16124||	if (op_type == IS_CONST) {
16125||		zval *zv = RT_CONSTANT(opline, op);
16126||		if (Z_TYPE_P(zv) == IS_DOUBLE && Z_DVAL_P(zv) != 0 && !IS_SIGNED_32BIT(zv)) {
16127||			return 1;
16128||		} else if (Z_TYPE_P(zv) == IS_LONG && !IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
16129||			return 1;
16130||		}
16131||	}
16132|.endif
16133	return 0;
16134}
16135
16136static 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)
16137{
16138	uint32_t op1_info, op2_info;
16139
16140	switch (opline->opcode) {
16141		case ZEND_FETCH_DIM_R:
16142			op1_info = OP1_INFO();
16143			op2_info = OP2_INFO();
16144			if (((opline->op1_type & (IS_TMP_VAR|IS_VAR)) &&
16145			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) ||
16146			    ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) &&
16147			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)))) {
16148				return ZEND_REGSET(ZREG_FCARG1);
16149			}
16150			break;
16151		default:
16152			break;
16153	}
16154
16155	return ZEND_REGSET_EMPTY;
16156}
16157
16158static 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)
16159{
16160	uint32_t op1_info, op2_info, res_info;
16161	zend_regset regset = ZEND_REGSET_SCRATCH;
16162
16163	switch (opline->opcode) {
16164		case ZEND_NOP:
16165		case ZEND_OP_DATA:
16166		case ZEND_JMP:
16167		case ZEND_RETURN:
16168			regset = ZEND_REGSET_EMPTY;
16169			break;
16170		case ZEND_QM_ASSIGN:
16171			if (ssa_op->op1_def == current_var ||
16172			    ssa_op->result_def == current_var) {
16173				regset = ZEND_REGSET_EMPTY;
16174				break;
16175			}
16176			/* break missing intentionally */
16177		case ZEND_SEND_VAL:
16178		case ZEND_SEND_VAL_EX:
16179			if (opline->op2_type == IS_CONST) {
16180				break;
16181			}
16182			if (ssa_op->op1_use == current_var) {
16183				regset = ZEND_REGSET(ZREG_R0);
16184				break;
16185			}
16186			op1_info = OP1_INFO();
16187			if (!(op1_info & MAY_BE_UNDEF)) {
16188				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
16189					regset = ZEND_REGSET(ZREG_XMM0);
16190				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
16191					regset = ZEND_REGSET(ZREG_R0);
16192				} else {
16193					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_R0), ZEND_REGSET(ZREG_R2));
16194				}
16195			}
16196			break;
16197		case ZEND_SEND_VAR:
16198			if (opline->op2_type == IS_CONST) {
16199				break;
16200			}
16201			if (ssa_op->op1_use == current_var ||
16202			    ssa_op->op1_def == current_var) {
16203				regset = ZEND_REGSET_EMPTY;
16204				break;
16205			}
16206			op1_info = OP1_INFO();
16207			if (!(op1_info & MAY_BE_UNDEF)) {
16208				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
16209					regset = ZEND_REGSET(ZREG_XMM0);
16210				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
16211				} else {
16212					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_R0), ZEND_REGSET(ZREG_R2));
16213					if (op1_info & MAY_BE_REF) {
16214						ZEND_REGSET_INCL(regset, ZREG_R1);
16215					}
16216				}
16217			}
16218			break;
16219		case ZEND_ASSIGN:
16220			if (ssa_op->op2_use == current_var ||
16221			    ssa_op->op2_def == current_var ||
16222			    ssa_op->op1_def == current_var ||
16223			    ssa_op->result_def == current_var) {
16224				regset = ZEND_REGSET_EMPTY;
16225				break;
16226			}
16227			op1_info = OP1_INFO();
16228			op2_info = OP2_INFO();
16229			if (opline->op1_type == IS_CV
16230			 && !(op2_info & MAY_BE_UNDEF)
16231			 && !(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
16232				if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
16233					regset = ZEND_REGSET(ZREG_XMM0);
16234				} else if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
16235					regset = ZEND_REGSET(ZREG_R0);
16236				} else {
16237					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_R0), ZEND_REGSET(ZREG_R2));
16238				}
16239			}
16240			break;
16241		case ZEND_PRE_INC:
16242		case ZEND_PRE_DEC:
16243		case ZEND_POST_INC:
16244		case ZEND_POST_DEC:
16245			if (ssa_op->op1_use == current_var ||
16246			    ssa_op->op1_def == current_var ||
16247			    ssa_op->result_def == current_var) {
16248				regset = ZEND_REGSET_EMPTY;
16249				break;
16250			}
16251			op1_info = OP1_INFO();
16252			if (opline->op1_type == IS_CV
16253			 && (op1_info & MAY_BE_LONG)
16254			 && !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
16255				regset = ZEND_REGSET_EMPTY;
16256				if (op1_info & MAY_BE_DOUBLE) {
16257					regset = ZEND_REGSET(ZREG_XMM0);
16258				}
16259				if (opline->result_type != IS_UNUSED && (op1_info & MAY_BE_LONG)) {
16260					ZEND_REGSET_INCL(regset, ZREG_R1);
16261				}
16262			}
16263			break;
16264		case ZEND_ADD:
16265		case ZEND_SUB:
16266		case ZEND_MUL:
16267			op1_info = OP1_INFO();
16268			op2_info = OP2_INFO();
16269			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
16270			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
16271
16272				regset = ZEND_REGSET_EMPTY;
16273				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
16274					if (ssa_op->result_def != current_var &&
16275					    (ssa_op->op1_use != current_var || !last_use)) {
16276						ZEND_REGSET_INCL(regset, ZREG_R0);
16277					}
16278					res_info = RES_INFO();
16279					if (res_info & MAY_BE_DOUBLE) {
16280						ZEND_REGSET_INCL(regset, ZREG_R0);
16281						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16282						ZEND_REGSET_INCL(regset, ZREG_XMM1);
16283					} else if (res_info & MAY_BE_GUARD) {
16284						ZEND_REGSET_INCL(regset, ZREG_R0);
16285					}
16286				}
16287				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
16288					if (opline->op1_type == IS_CONST) {
16289						ZEND_REGSET_INCL(regset, ZREG_R0);
16290					}
16291					if (ssa_op->result_def != current_var) {
16292						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16293					}
16294				}
16295				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
16296					if (opline->op2_type == IS_CONST) {
16297						ZEND_REGSET_INCL(regset, ZREG_R0);
16298					}
16299					if (zend_is_commutative(opline->opcode)) {
16300						if (ssa_op->result_def != current_var) {
16301							ZEND_REGSET_INCL(regset, ZREG_XMM0);
16302						}
16303					} else {
16304						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16305						if (ssa_op->result_def != current_var &&
16306						    (ssa_op->op1_use != current_var || !last_use)) {
16307							ZEND_REGSET_INCL(regset, ZREG_XMM1);
16308						}
16309					}
16310				}
16311				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
16312					if (ssa_op->result_def != current_var &&
16313					    (ssa_op->op1_use != current_var || !last_use) &&
16314					    (!zend_is_commutative(opline->opcode) || ssa_op->op2_use != current_var || !last_use)) {
16315						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16316					}
16317				}
16318				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
16319				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
16320					if (!ZEND_REGSET_IN(regset, ZREG_R0)) {
16321						ZEND_REGSET_INCL(regset, ZREG_R0);
16322					} else {
16323						ZEND_REGSET_INCL(regset, ZREG_R1);
16324					}
16325				}
16326			}
16327			break;
16328		case ZEND_BW_OR:
16329		case ZEND_BW_AND:
16330		case ZEND_BW_XOR:
16331			op1_info = OP1_INFO();
16332			op2_info = OP2_INFO();
16333			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
16334			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
16335				regset = ZEND_REGSET_EMPTY;
16336				if (ssa_op->result_def != current_var &&
16337				    (ssa_op->op1_use != current_var || !last_use)) {
16338					ZEND_REGSET_INCL(regset, ZREG_R0);
16339				}
16340				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
16341				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
16342					if (!ZEND_REGSET_IN(regset, ZREG_R0)) {
16343						ZEND_REGSET_INCL(regset, ZREG_R0);
16344					} else {
16345						ZEND_REGSET_INCL(regset, ZREG_R1);
16346					}
16347				}
16348			}
16349			break;
16350		case ZEND_SL:
16351		case ZEND_SR:
16352			op1_info = OP1_INFO();
16353			op2_info = OP2_INFO();
16354			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
16355			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
16356bw_op:
16357				regset = ZEND_REGSET_EMPTY;
16358				if (ssa_op->result_def != current_var &&
16359				    (ssa_op->op1_use != current_var || !last_use)) {
16360					ZEND_REGSET_INCL(regset, ZREG_R0);
16361				}
16362				if (opline->op2_type != IS_CONST && ssa_op->op2_use != current_var) {
16363					ZEND_REGSET_INCL(regset, ZREG_R1);
16364				}
16365			}
16366			break;
16367		case ZEND_MOD:
16368			op1_info = OP1_INFO();
16369			op2_info = OP2_INFO();
16370			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
16371			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
16372				if (opline->op2_type == IS_CONST &&
16373				    opline->op1_type != IS_CONST &&
16374				    Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG &&
16375				    zend_long_is_power_of_two(Z_LVAL_P(RT_CONSTANT(opline, opline->op2))) &&
16376				    OP1_HAS_RANGE() &&
16377				    OP1_MIN_RANGE() >= 0) {
16378				    /* MOD is going to be optimized into AND */
16379				    goto bw_op;
16380				} else {
16381					regset = ZEND_REGSET_EMPTY;
16382					ZEND_REGSET_INCL(regset, ZREG_R0);
16383					ZEND_REGSET_INCL(regset, ZREG_R2);
16384					if (opline->op2_type == IS_CONST) {
16385						ZEND_REGSET_INCL(regset, ZREG_R1);
16386					}
16387				}
16388			}
16389			break;
16390		case ZEND_IS_SMALLER:
16391		case ZEND_IS_SMALLER_OR_EQUAL:
16392		case ZEND_IS_EQUAL:
16393		case ZEND_IS_NOT_EQUAL:
16394		case ZEND_IS_IDENTICAL:
16395		case ZEND_IS_NOT_IDENTICAL:
16396		case ZEND_CASE:
16397			op1_info = OP1_INFO();
16398			op2_info = OP2_INFO();
16399			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
16400			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
16401				regset = ZEND_REGSET_EMPTY;
16402				if (!(opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ))) {
16403					ZEND_REGSET_INCL(regset, ZREG_R0);
16404				}
16405				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) &&
16406				    opline->op1_type != IS_CONST && opline->op2_type != IS_CONST) {
16407					if (ssa_op->op1_use != current_var &&
16408					    ssa_op->op2_use != current_var) {
16409						ZEND_REGSET_INCL(regset, ZREG_R0);
16410					}
16411				}
16412				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
16413					ZEND_REGSET_INCL(regset, ZREG_XMM0);
16414				}
16415				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
16416					ZEND_REGSET_INCL(regset, ZREG_XMM0);
16417				}
16418				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
16419					if (ssa_op->op1_use != current_var &&
16420					    ssa_op->op2_use != current_var) {
16421						ZEND_REGSET_INCL(regset, ZREG_XMM0);
16422					}
16423				}
16424				if (zend_needs_extra_reg_for_const(opline, opline->op1_type, opline->op1) ||
16425				    zend_needs_extra_reg_for_const(opline, opline->op2_type, opline->op2)) {
16426					ZEND_REGSET_INCL(regset, ZREG_R0);
16427				}
16428			}
16429			break;
16430		case ZEND_BOOL:
16431		case ZEND_BOOL_NOT:
16432		case ZEND_JMPZ:
16433		case ZEND_JMPNZ:
16434		case ZEND_JMPZNZ:
16435		case ZEND_JMPZ_EX:
16436		case ZEND_JMPNZ_EX:
16437			op1_info = OP1_INFO();
16438			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)))) {
16439				regset = ZEND_REGSET_EMPTY;
16440				if (op1_info & MAY_BE_DOUBLE) {
16441					ZEND_REGSET_INCL(regset, ZREG_XMM0);
16442				}
16443				if (opline->opcode == ZEND_BOOL ||
16444				    opline->opcode == ZEND_BOOL_NOT ||
16445				    opline->opcode == ZEND_JMPZ_EX ||
16446				    opline->opcode == ZEND_JMPNZ_EX) {
16447					ZEND_REGSET_INCL(regset, ZREG_R0);
16448				}
16449			}
16450			break;
16451		case ZEND_DO_UCALL:
16452		case ZEND_DO_FCALL:
16453		case ZEND_DO_FCALL_BY_NAME:
16454		case ZEND_INCLUDE_OR_EVAL:
16455		case ZEND_GENERATOR_CREATE:
16456		case ZEND_YIELD:
16457		case ZEND_YIELD_FROM:
16458			regset = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP);
16459			break;
16460		default:
16461			break;
16462	}
16463
16464	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
16465		if (ssa_op == ssa->ops
16466		 && JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].op == ZEND_JIT_TRACE_INIT_CALL
16467		 && (JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
16468			ZEND_REGSET_INCL(regset, ZREG_R0);
16469			ZEND_REGSET_INCL(regset, ZREG_R1);
16470		}
16471	}
16472
16473	/* %r0 is used to check EG(vm_interrupt) */
16474	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
16475		if (ssa_op == ssa->ops
16476		 && (JIT_G(current_trace)->stop == ZEND_JIT_TRACE_STOP_LOOP ||
16477			 JIT_G(current_trace)->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL)) {
16478#if ZTS
16479			ZEND_REGSET_INCL(regset, ZREG_R0);
16480#else
16481			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(vm_interrupt)))) {
16482				ZEND_REGSET_INCL(regset, ZREG_R0);
16483			}
16484#endif
16485		}
16486	} else  {
16487		uint32_t b = ssa->cfg.map[ssa_op - ssa->ops];
16488
16489		if ((ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) != 0
16490		 && ssa->cfg.blocks[b].start == ssa_op - ssa->ops) {
16491#if ZTS
16492			ZEND_REGSET_INCL(regset, ZREG_R0);
16493#else
16494			if ((sizeof(void*) == 8 && !IS_SIGNED_32BIT(&EG(vm_interrupt)))) {
16495				ZEND_REGSET_INCL(regset, ZREG_R0);
16496			}
16497#endif
16498		}
16499	}
16500
16501	return regset;
16502}
16503
16504/*
16505 * Local variables:
16506 * tab-width: 4
16507 * c-basic-offset: 4
16508 * indent-tabs-mode: t
16509 * End:
16510 */
16511