xref: /PHP-8.1/ext/opcache/jit/zend_jit_arm64.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 *  |          Hao Sun <hao.sun@arm.com>                                   |
18 *  +----------------------------------------------------------------------+
19 */
20
21|.arch arm64
22
23|.define FP,      x27
24|.define IP,      x28
25|.define IPl,     w28
26|.define RX,      x28         // the same as VM IP reused as a general purpose reg
27|.define LR,      x30
28|.define CARG1,   x0
29|.define CARG2,   x1
30|.define CARG3,   x2
31|.define CARG4,   x3
32|.define CARG5,   x4
33|.define CARG6,   x5
34|.define CARG1w,  w0
35|.define CARG2w,  w1
36|.define CARG3w,  w2
37|.define CARG4w,  w3
38|.define CARG5w,  w4
39|.define CARG6w,  w5
40|.define RETVALx, x0
41|.define RETVALw, w0
42|.define FCARG1x, x0
43|.define FCARG1w, w0
44|.define FCARG2x, x1
45|.define FCARG2w, w1
46|.define SPAD,    0x20        // padding for CPU stack alignment
47|.define NR_SPAD, 0x30        // padding for CPU stack alignment
48|.define T3,      [sp, #0x28] // Used to store old value of IP (CALL VM only)
49|.define T2,      [sp, #0x20] // Used to store old value of FP (CALL VM only)
50|.define T1,      [sp, #0x10]
51
52// We use REG0/1/2 and FPR0/1 to replace r0/1/2 and xmm0/1 in the x86 implementation.
53// Scratch registers
54|.define REG0,    x8
55|.define REG0w,   w8
56|.define REG1,    x9
57|.define REG1w,   w9
58|.define REG2,    x10
59|.define REG2w,   w10
60|.define FPR0,    d0
61|.define FPR1,    d1
62
63|.define ZREG_REG0,   ZREG_X8
64|.define ZREG_REG1,   ZREG_X9
65|.define ZREG_REG2,   ZREG_X10
66|.define ZREG_FPR0,   ZREG_V0
67|.define ZREG_FPR1,   ZREG_V1
68
69// Temporaries, not preserved across calls
70|.define TMP1,    x15
71|.define TMP1w,   w15
72|.define TMP2,    x16
73|.define TMP2w,   w16
74|.define TMP3,    x17 // TODO: remember about hard-coded: mrs TMP3, tpidr_el0
75|.define TMP3w,   w17
76|.define FPTMP,   d16
77
78|.define ZREG_TMP1,   ZREG_X15
79|.define ZREG_TMP2,   ZREG_X16
80|.define ZREG_TMP3,   ZREG_X17
81|.define ZREG_FPTMP,  ZREG_V16
82
83|.define HYBRID_SPAD, 32     // padding for stack alignment
84
85#define TMP_ZVAL_OFFSET 16
86#define DASM_ALIGNMENT  16
87
88const char* zend_reg_name[] = {
89	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
90	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
91	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
92	"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
93	"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
94	"d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
95	"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
96	"d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
97};
98
99#define ZREG_FCARG1 ZREG_X0
100#define ZREG_FCARG2 ZREG_X1
101
102|.type EX, zend_execute_data, FP
103|.type OP, zend_op
104|.type ZVAL, zval
105|.actionlist dasm_actions
106|.globals zend_lb
107|.section code, cold_code, jmp_table
108
109static void* dasm_labels[zend_lb_MAX];
110
111#if ZTS
112static size_t tsrm_ls_cache_tcb_offset = 0;
113# ifdef __APPLE__
114struct TLVDescriptor {
115	void*       (*thunk)(struct TLVDescriptor*);
116	uint64_t    key;
117	uint64_t    offset;
118};
119typedef struct TLVDescriptor TLVDescriptor;
120# endif
121#endif
122
123#define IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1)))
124
125/* Encoding of immediate. */
126#define MAX_IMM12       0xfff          // maximum value for imm12
127#define MAX_IMM16       0xffff         // maximum value for imm16
128#define MOVZ_IMM        MAX_IMM16      // movz insn
129#define LDR_STR_PIMM64  (MAX_IMM12*8)  // ldr/str insn for 64-bit register: pimm is imm12 * 8
130#define LDR_STR_PIMM32  (MAX_IMM12*4)  // ldr/str insn for 32-bit register: pimm is imm12 * 4
131#define LDRB_STRB_PIMM  MAX_IMM12      // ldrb/strb insn
132
133#define B_IMM           (1<<27)        // signed imm26 * 4
134#define ADR_IMM         (1<<20)        // signed imm21
135#define ADRP_IMM        (1LL<<32)      // signed imm21 * 4096
136
137static bool arm64_may_use_b(const void *addr)
138{
139	if (addr >= dasm_buf && addr < dasm_end) {
140		return (((char*)dasm_end - (char*)dasm_buf) < B_IMM);
141	} else if (addr >= dasm_end) {
142		return (((char*)addr - (char*)dasm_buf) < B_IMM);
143	} else if (addr < dasm_buf) {
144		return (((char*)dasm_end - (char*)addr) < B_IMM);
145	}
146	return 0;
147}
148
149static bool arm64_may_use_adr(const void *addr)
150{
151	if (addr >= dasm_buf && addr < dasm_end) {
152		return (((char*)dasm_end - (char*)dasm_buf) < ADR_IMM);
153	} else if (addr >= dasm_end) {
154		return (((char*)addr - (char*)dasm_buf) < ADR_IMM);
155	} else if (addr < dasm_buf) {
156		return (((char*)dasm_end - (char*)addr) < ADR_IMM);
157	}
158	return 0;
159}
160
161static bool arm64_may_use_adrp(const void *addr)
162{
163	if (addr >= dasm_buf && addr < dasm_end) {
164		return (((char*)dasm_end - (char*)dasm_buf) < ADRP_IMM);
165	} else if (addr >= dasm_end) {
166		return (((char*)addr - (char*)dasm_buf) < ADRP_IMM);
167	} else if (addr < dasm_buf) {
168		return (((char*)dasm_end - (char*)addr) < ADRP_IMM);
169	}
170	return 0;
171}
172
173/* Determine whether "val" falls into two allowed ranges:
174 *   Range 1: [0, 0xfff]
175 *   Range 2: LSL #12 to Range 1
176 * Used to guard the immediate encoding for add/adds/sub/subs/cmp/cmn instructions. */
177static bool arm64_may_encode_imm12(const int64_t val)
178{
179	return (val >= 0 && (val <= MAX_IMM12 || !(val & 0xffffffffff000fff)));
180}
181
182/* Determine whether an immediate value can be encoded as the immediate operand of logical instructions. */
183static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
184{
185	/* fast path: power of two */
186	if (value > 0 && !(value & (value - 1))) {
187		return true;
188	}
189
190	if (reg_size == 32) {
191		if (dasm_imm13((uint32_t)value, (uint32_t)value) != -1) {
192			return true;
193		}
194	} else if (reg_size == 64) {
195		if (dasm_imm13((uint32_t)value, (uint32_t)(value >> 32)) != -1) {
196			return true;
197		}
198	} else {
199		ZEND_UNREACHABLE();
200	}
201
202	return false;
203}
204
205/* Not Implemented Yet */
206|.macro NIY
207||	//ZEND_ASSERT(0);
208|	brk #0
209|.endmacro
210
211|.macro NIY_STUB
212||	//ZEND_ASSERT(0);
213|	brk #0
214|.endmacro
215
216|.macro ADD_HYBRID_SPAD
217||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
218|	add sp, sp, # HYBRID_SPAD
219||#endif
220|.endmacro
221
222|.macro SUB_HYBRID_SPAD
223||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
224|	sub sp, sp, # HYBRID_SPAD
225||#endif
226|.endmacro
227
228/* Move address into register. TODO: Support 52-bit address */
229|.macro LOAD_ADDR, reg, addr
230|	// 48-bit virtual address
231||	if (((uintptr_t)(addr)) == 0) {
232|		mov reg, xzr
233||	} else if (((uintptr_t)(addr)) <= MOVZ_IMM) {
234|		movz reg, #((uint64_t)(addr))
235||	} else if (arm64_may_use_adr((void*)(addr))) {
236|		adr reg, &addr
237||	} else if (arm64_may_use_adrp((void*)(addr))) {
238|		adrp reg, &(((uintptr_t)(addr)))
239||		if (((uintptr_t)(addr)) & 0xfff) {
240|			add reg, reg, #(((uintptr_t)(addr)) & 0xfff)
241||		}
242||	} else if ((uintptr_t)(addr) & 0xffff) {
243|		movz reg, #((uintptr_t)(addr) & 0xffff)
244||		if (((uintptr_t)(addr) >> 16) & 0xffff) {
245|			movk reg, #(((uintptr_t)(addr) >> 16) & 0xffff), lsl #16
246||		}
247||		if (((uintptr_t)(addr) >> 32) & 0xffff) {
248|			movk reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32
249||		}
250||	} else if (((uintptr_t)(addr) >> 16) & 0xffff) {
251|		movz reg, #(((uintptr_t)(addr) >> 16) & 0xffff), lsl #16
252||		if (((uintptr_t)(addr) >> 32) & 0xffff) {
253|			movk reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32
254||		}
255||	} else {
256|		movz reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32
257||	}
258|.endmacro
259
260/* Move 32-bit immediate value into register. */
261|.macro LOAD_32BIT_VAL, reg, val
262||	if (((uint32_t)(val)) <= MOVZ_IMM) {
263|		movz reg, #((uint32_t)(val))
264||	} else if (((uint32_t)(val) & 0xffff)) {
265|		movz reg, #((uint32_t)(val) & 0xffff)
266||		if ((((uint32_t)(val) >> 16) & 0xffff)) {
267|			movk reg, #(((uint32_t)(val) >> 16) & 0xffff), lsl #16
268||		}
269||	} else {
270|		movz reg, #(((uint32_t)(val) >> 16) & 0xffff), lsl #16
271||	}
272|.endmacro
273
274/* Move 64-bit immediate value into register. */
275|.macro LOAD_64BIT_VAL, reg, val
276||	if (((uint64_t)(val)) == 0) {
277|		mov reg, xzr
278||	} else if (((uint64_t)(val)) <= MOVZ_IMM) {
279|		movz reg, #((uint64_t)(val))
280||	} else if (~((uint64_t)(val)) <= MOVZ_IMM) {
281|		movn reg, #(~((uint64_t)(val)))
282||	} else if ((uint64_t)(val) & 0xffff) {
283|		movz reg, #((uint64_t)(val) & 0xffff)
284||		if (((uint64_t)(val) >> 16) & 0xffff) {
285|			movk reg, #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
286||		}
287||		if (((uint64_t)(val) >> 32) & 0xffff) {
288|			movk reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
289||		}
290||		if ((((uint64_t)(val) >> 48) & 0xffff)) {
291|			movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
292||		}
293||	} else if (((uint64_t)(val) >> 16) & 0xffff) {
294|		movz reg, #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
295||		if (((uint64_t)(val) >> 32) & 0xffff) {
296|			movk reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
297||		}
298||		if ((((uint64_t)(val) >> 48) & 0xffff)) {
299|			movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
300||		}
301||	} else if (((uint64_t)(val) >> 32) & 0xffff) {
302|		movz reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
303||		if ((((uint64_t)(val) >> 48) & 0xffff)) {
304|			movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
305||		}
306||	} else {
307|		movz reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
308||	}
309|.endmacro
310
311/* Extract the low 8 bits from 'src_reg' into 'dst_reg'.
312 * Note: 0xff can be encoded as imm for 'and' instruction. */
313|.macro GET_LOW_8BITS, dst_reg, src_reg
314|	and dst_reg, src_reg, #0xff
315|.endmacro
316
317/* Bitwise operation with immediate. 'bw_ins' can be and/orr/eor/ands.
318 * 32-bit and 64-bit registers are distinguished. */
319|.macro BW_OP_32_WITH_CONST, bw_ins, dst_reg, src_reg1, val, tmp_reg
320||	if (val == 0) {
321|		bw_ins dst_reg, src_reg1, wzr
322||	} else if (logical_immediate_p((uint32_t)val, 32)) {
323|		bw_ins dst_reg, src_reg1, #val
324||	} else {
325|		LOAD_32BIT_VAL tmp_reg, val
326|		bw_ins dst_reg, src_reg1, tmp_reg
327||	}
328|.endmacro
329
330|.macro BW_OP_64_WITH_CONST, bw_ins, dst_reg, src_reg1, val, tmp_reg
331||	if (val == 0) {
332|		bw_ins dst_reg, src_reg1, xzr
333||	} else if (logical_immediate_p(val, 64)) {
334|		bw_ins dst_reg, src_reg1, #val
335||	} else {
336|		LOAD_64BIT_VAL tmp_reg, val
337|		bw_ins dst_reg, src_reg1, tmp_reg
338||	}
339|.endmacro
340
341/* Test bits 'tst' with immediate. 32-bit and 64-bit registers are distinguished. */
342|.macro TST_32_WITH_CONST, reg, val, tmp_reg
343||	if (val == 0) {
344|		tst reg, wzr
345||	} else if (logical_immediate_p((uint32_t)val, 32)) {
346|		tst reg, #val
347||	} else {
348|		LOAD_32BIT_VAL tmp_reg, val
349|		tst reg, tmp_reg
350||	}
351|.endmacro
352
353|.macro TST_64_WITH_CONST, reg, val, tmp_reg
354||	if (val == 0) {
355|		tst reg, xzr
356||	} else if (logical_immediate_p(val, 64)) {
357|		tst reg, #val
358||	} else {
359|		LOAD_64BIT_VAL tmp_reg, val
360|		tst reg, tmp_reg
361||	}
362|.endmacro
363
364/* Test bits between 64-bit register with constant 1. */
365|.macro TST_64_WITH_ONE, reg
366|	tst reg, #1
367|.endmacro
368
369/* Compare a register value with immediate. 32-bit and 64-bit registers are distinguished.
370 * Note: Comparing 64-bit register with 32-bit immediate is handled in CMP_64_WITH_CONST_32. */
371|.macro CMP_32_WITH_CONST, reg, val, tmp_reg
372||	if (val == 0) {
373|		cmp reg, wzr
374||	} else if (arm64_may_encode_imm12((int64_t)(val))) {
375|		cmp reg, #val
376||	} else if (arm64_may_encode_imm12((int64_t)(-val))) {
377|		cmn reg, #-val
378||	} else {
379|		LOAD_32BIT_VAL tmp_reg, val
380|		cmp reg, tmp_reg
381||	}
382|.endmacro
383
384|.macro CMP_64_WITH_CONST_32, reg, val, tmp_reg
385||	if (val == 0) {
386|		cmp reg, xzr
387||	} else if (arm64_may_encode_imm12((int64_t)(val))) {
388|		cmp reg, #val
389||	} else if (arm64_may_encode_imm12((int64_t)(-val))) {
390|		cmn reg, #-val
391||	} else {
392|		LOAD_32BIT_VAL tmp_reg, val
393|		cmp reg, tmp_reg
394||	}
395|.endmacro
396
397|.macro CMP_64_WITH_CONST, reg, val, tmp_reg
398||	if (val == 0) {
399|		cmp reg, xzr
400||	} else if (arm64_may_encode_imm12((int64_t)(val))) {
401|		cmp reg, #val
402||	} else if (arm64_may_encode_imm12((int64_t)(-val))) {
403|		cmn reg, #-val
404||	} else {
405|		LOAD_64BIT_VAL tmp_reg, val
406|		cmp reg, tmp_reg
407||	}
408|.endmacro
409
410/* Add/sub a register value with immediate. 'add_sub_ins' can be add/sub/adds/subs.
411 * 32-bit and 64-bit registers are distinguished.
412 * Note: Case of 64-bit register with 32-bit immediate is handled in ADD_SUB_64_WITH_CONST_32. */
413|.macro ADD_SUB_32_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
414||	if (val == 0) {
415|		add_sub_ins dst_reg, src_reg1, wzr
416||	} else if (arm64_may_encode_imm12((int64_t)(val))) {
417|		add_sub_ins dst_reg, src_reg1, #val
418||	} else {
419|		LOAD_32BIT_VAL tmp_reg, val
420|		add_sub_ins dst_reg, src_reg1, tmp_reg
421||	}
422|.endmacro
423
424|.macro ADD_SUB_64_WITH_CONST_32, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
425||	if (val == 0) {
426|		add_sub_ins dst_reg, src_reg1, xzr
427||	} else if (arm64_may_encode_imm12((int64_t)(val))) {
428|		add_sub_ins dst_reg, src_reg1, #val
429||	} else {
430|		LOAD_32BIT_VAL tmp_reg, val
431|		add_sub_ins dst_reg, src_reg1, tmp_reg
432||	}
433|.endmacro
434
435|.macro ADD_SUB_64_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
436||	if (val == 0) {
437|		add_sub_ins dst_reg, src_reg1, xzr
438||	} else if (arm64_may_encode_imm12((int64_t)(val))) {
439|		add_sub_ins dst_reg, src_reg1, #val
440||	} else {
441|		LOAD_64BIT_VAL tmp_reg, val
442|		add_sub_ins dst_reg, src_reg1, tmp_reg
443||	}
444|.endmacro
445
446/* Memory access(load/store) with 32-bit 'offset'.
447 * Use "unsigned offset" variant if 'offset' can be encoded, otherwise move 'offset' into temp register.
448 * 8-bit, 32-bit and 64-bit registers to be transferred are distinguished.
449 * Note: 'reg' can be used as 'tmp_reg' if 1) 'reg' is one GPR, AND 2) 'reg' != 'base_reg', AND 3) ins is 'ldr'. */
450|.macro MEM_ACCESS_64_WITH_UOFFSET, ldr_str_ins, reg, base_reg, offset, tmp_reg
451||	if (((uintptr_t)(offset)) > LDR_STR_PIMM64) {
452|		LOAD_32BIT_VAL tmp_reg, offset
453|		ldr_str_ins reg, [base_reg, tmp_reg]
454||	} else {
455|		ldr_str_ins reg, [base_reg, #(offset)]
456||	}
457|.endmacro
458
459|.macro MEM_ACCESS_32_WITH_UOFFSET, ldr_str_ins, reg, base_reg, offset, tmp_reg
460||	if (((uintptr_t)(offset)) > LDR_STR_PIMM32) {
461|		LOAD_32BIT_VAL tmp_reg, offset
462|		ldr_str_ins reg, [base_reg, tmp_reg]
463||	} else {
464|		ldr_str_ins reg, [base_reg, #(offset)]
465||	}
466|.endmacro
467
468|.macro MEM_ACCESS_8_WITH_UOFFSET, ldrb_strb_ins, reg, base_reg, offset, tmp_reg
469||	if (((uintptr_t)(offset)) > LDRB_STRB_PIMM) {
470|		LOAD_32BIT_VAL tmp_reg, offset
471|		ldrb_strb_ins reg, [base_reg, tmp_reg]
472||	} else {
473|		ldrb_strb_ins reg, [base_reg, #(offset)]
474||	}
475|.endmacro
476
477/* Memory access(load/store) with 64-bit 'offset'.
478 * Use "unsigned offset" variant if 'offset' can be encoded, otherwise move 'offset' into temp register. */
479|.macro MEM_ACCESS_64_WITH_UOFFSET_64, ldr_str_ins, reg, base_reg, offset, tmp_reg
480||	if (((uintptr_t)(offset)) > LDR_STR_PIMM64) {
481|		LOAD_64BIT_VAL tmp_reg, offset
482|		ldr_str_ins reg, [base_reg, tmp_reg]
483||	} else {
484|		ldr_str_ins reg, [base_reg, #(offset)]
485||	}
486|.endmacro
487
488/* ZTS: get thread local variable "_tsrm_ls_cache" */
489|.macro LOAD_TSRM_CACHE, reg
490||#ifdef __APPLE__
491|	.long 0xd53bd071 // TODO: hard-coded: mrs TMP3, tpidrro_el0
492|	and TMP3, TMP3, #0xfffffffffffffff8
493|	MEM_ACCESS_64_WITH_UOFFSET_64 ldr, TMP3, TMP3, (((TLVDescriptor*)tsrm_ls_cache_tcb_offset)->key << 3), TMP1
494|	MEM_ACCESS_64_WITH_UOFFSET_64 ldr, reg, TMP3, (((TLVDescriptor*)tsrm_ls_cache_tcb_offset)->offset), TMP1
495||#else
496|	.long 0xd53bd051 // TODO: hard-coded: mrs TMP3, tpidr_el0
497||	ZEND_ASSERT(tsrm_ls_cache_tcb_offset <= LDR_STR_PIMM64);
498|	ldr reg, [TMP3, #tsrm_ls_cache_tcb_offset]
499||#endif
500|.endmacro
501
502|.macro LOAD_ADDR_ZTS, reg, struct, field
503|	.if ZTS
504|		LOAD_TSRM_CACHE TMP3
505|		ADD_SUB_64_WITH_CONST_32 add, reg, TMP3, (struct.._offset + offsetof(zend_..struct, field)), reg
506|	.else
507|		LOAD_ADDR reg, &struct.field
508|	.endif
509|.endmacro
510
511/* Store address 'addr' into memory 'mem'. */
512|.macro ADDR_STORE, mem, addr, tmp_reg
513|	LOAD_ADDR tmp_reg, addr
514|	str tmp_reg, mem
515|.endmacro
516
517/* Store a register value 'reg' into memory 'addr'.
518 * For ZTS mode, base register with unsigned offset variant is used,
519 * and 8-bit/32-bit/64-bit registers to be transferred are distinguished. */
520|.macro MEM_STORE, str_ins, reg, addr, tmp_reg
521||	if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adr((void*)(addr))) {
522|		adr tmp_reg, &addr
523|		str_ins reg, [tmp_reg]
524||	} else if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adrp((void*)(addr))) {
525|		adrp tmp_reg, &(((uintptr_t)(addr)))
526|		str_ins reg, [tmp_reg, #(((uintptr_t)(addr)) & 0xfff)]
527||	} else {
528|		LOAD_ADDR tmp_reg, addr
529|		str_ins reg, [tmp_reg]
530||	}
531|.endmacro
532
533|.macro MEM_STORE_64_ZTS, str_ins, reg, struct, field, tmp_reg
534|	.if ZTS
535|		LOAD_TSRM_CACHE TMP3
536|		MEM_ACCESS_64_WITH_UOFFSET str_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
537|	.else
538|		MEM_STORE str_ins, reg, &struct.field, tmp_reg
539|	.endif
540|.endmacro
541
542|.macro MEM_STORE_32_ZTS, str_ins, reg, struct, field, tmp_reg
543|	.if ZTS
544|		LOAD_TSRM_CACHE TMP3
545|		MEM_ACCESS_32_WITH_UOFFSET str_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
546|	.else
547|		MEM_STORE str_ins, reg, &struct.field, tmp_reg
548|	.endif
549|.endmacro
550
551|.macro MEM_STORE_8_ZTS, strb_ins, reg, struct, field, tmp_reg
552|	.if ZTS
553|		LOAD_TSRM_CACHE TMP3
554|		MEM_ACCESS_8_WITH_UOFFSET strb_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
555|	.else
556|		MEM_STORE strb_ins, reg, &struct.field, tmp_reg
557|	.endif
558|.endmacro
559
560/* Load value from memory 'addr' and write it into register 'reg'.
561 * For ZTS mode, base register with unsigned offset variant is used,
562 * and 8-bit/32-bit/64-bit registers to be transferred are distinguished. */
563|.macro MEM_LOAD, ldr_ins, reg, addr, tmp_reg
564||	if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adr((void*)(addr))) {
565|		adr tmp_reg, &addr
566|		ldr_ins reg, [tmp_reg]
567||	} else if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adrp((void*)(addr))) {
568|		adrp tmp_reg, &(((uintptr_t)(addr)))
569|		ldr_ins reg, [tmp_reg, #(((uintptr_t)(addr)) & 0xfff)]
570||	} else {
571|		LOAD_ADDR tmp_reg, addr
572|		ldr_ins reg, [tmp_reg]
573||	}
574|.endmacro
575
576|.macro MEM_LOAD_64_ZTS, ldr_ins, reg, struct, field, tmp_reg
577|	.if ZTS
578|		LOAD_TSRM_CACHE TMP3
579|		MEM_ACCESS_64_WITH_UOFFSET ldr_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
580|	.else
581|		MEM_LOAD ldr_ins, reg, &struct.field, tmp_reg
582|	.endif
583|.endmacro
584
585|.macro MEM_LOAD_32_ZTS, ldr_ins, reg, struct, field, tmp_reg
586|	.if ZTS
587|		LOAD_TSRM_CACHE TMP3
588|		MEM_ACCESS_32_WITH_UOFFSET ldr_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
589|	.else
590|		MEM_LOAD ldr_ins, reg, &struct.field, tmp_reg
591|	.endif
592|.endmacro
593
594|.macro MEM_LOAD_8_ZTS, ldrb_ins, reg, struct, field, tmp_reg
595|	.if ZTS
596|		LOAD_TSRM_CACHE TMP3
597|		MEM_ACCESS_8_WITH_UOFFSET ldrb_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
598|	.else
599|		MEM_LOAD ldrb_ins, reg, &struct.field, tmp_reg
600|	.endif
601|.endmacro
602
603/* Conduct arithmetic operation between the value in memory 'addr' and register value in 'reg',
604 * and the computation result is stored back in 'reg'. 'op_ins' can be add/sub. */
605|.macro MEM_LOAD_OP, op_ins, ldr_ins, reg, addr, tmp_reg1, tmp_reg2
606|	MEM_LOAD ldr_ins, tmp_reg1, addr, tmp_reg2
607|	op_ins reg, reg, tmp_reg1
608|.endmacro
609
610|.macro MEM_LOAD_OP_ZTS, op_ins, ldr_ins, reg, struct, field, tmp_reg1, tmp_reg2
611|	.if ZTS
612|		LOAD_TSRM_CACHE TMP3
613|		MEM_ACCESS_64_WITH_UOFFSET ldr_ins, tmp_reg2, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg1
614|		op_ins reg, reg, tmp_reg2
615|	.else
616|		MEM_LOAD_OP op_ins, ldr_ins, reg, &struct.field, tmp_reg1, tmp_reg2
617|	.endif
618|.endmacro
619
620/* Conduct arithmetic operation between the value in memory 'addr' and operand 'op', and the computation
621 * result is stored back to memory 'addr'. Operand 'op' can be either a register value or an immediate value.
622 * Currently, only add instruction is used as 'op_ins'.
623 * Note: It should be guaranteed that the immediate value can be encoded for 'op_ins'. */
624|.macro MEM_UPDATE, op_ins, ldr_ins, str_ins, op, addr, tmp_reg1, tmp_reg2
625|	LOAD_ADDR tmp_reg2, addr
626|	ldr_ins, tmp_reg1, [tmp_reg2]
627|	op_ins tmp_reg1, tmp_reg1, op
628|	str_ins tmp_reg1, [tmp_reg2]
629|.endmacro
630
631|.macro MEM_UPDATE_ZTS, op_ins, ldr_ins, str_ins, op, struct, field, tmp_reg1, tmp_reg2
632|	.if ZTS
633|		LOAD_TSRM_CACHE TMP3
634||		if (((uintptr_t)(struct.._offset+offsetof(zend_..struct, field))) > LDR_STR_PIMM64) {
635|			LOAD_32BIT_VAL tmp_reg1, (struct.._offset+offsetof(zend_..struct, field))
636|			ldr_ins tmp_reg2, [TMP3, tmp_reg1]
637|			op_ins tmp_reg2, tmp_reg2, op
638|			str_ins tmp_reg2, [TMP3, tmp_reg1]
639||		} else {
640|			ldr_ins tmp_reg2, [TMP3, #(struct.._offset+offsetof(zend_..struct, field))]
641|			op_ins tmp_reg2, tmp_reg2, op
642|			str_ins tmp_reg2, [TMP3, #(struct.._offset+offsetof(zend_..struct, field))]
643||		}
644|	.else
645|		MEM_UPDATE op_ins, ldr_ins, str_ins, op, &struct.field, tmp_reg1, tmp_reg2
646|	.endif
647|.endmacro
648
649|.macro EXT_CALL, func, tmp_reg
650||	if (arm64_may_use_b(func)) {
651|		bl &func
652||	} else {
653|		LOAD_ADDR tmp_reg, func
654|		blr tmp_reg
655||	}
656|.endmacro
657
658|.macro EXT_JMP, func, tmp_reg
659||	if (arm64_may_use_b(func)) {
660|		b &func
661||	} else {
662|		LOAD_ADDR tmp_reg, func
663|		br tmp_reg
664||	}
665|.endmacro
666
667|.macro SAVE_IP
668||	if (GCC_GLOBAL_REGS) {
669|		str IP, EX->opline
670||	}
671|.endmacro
672
673|.macro LOAD_IP
674||	if (GCC_GLOBAL_REGS) {
675|		ldr IP, EX->opline
676||	}
677|.endmacro
678
679|.macro LOAD_IP_ADDR, addr
680||	if (GCC_GLOBAL_REGS) {
681|		LOAD_ADDR IP, addr
682||	} else {
683|		ADDR_STORE EX->opline, addr, RX
684||	}
685|.endmacro
686
687|.macro LOAD_IP_ADDR_ZTS, struct, field, tmp_reg
688|	.if ZTS
689||		if (GCC_GLOBAL_REGS) {
690|			LOAD_TSRM_CACHE IP
691|	   		MEM_ACCESS_64_WITH_UOFFSET ldr, IP, IP, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
692||		} else {
693|			LOAD_TSRM_CACHE RX
694|			ADD_SUB_64_WITH_CONST_32 add, RX, RX, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg
695|			str RX, EX->opline
696||		}
697|	.else
698|		LOAD_IP_ADDR &struct.field
699|	.endif
700|.endmacro
701
702|.macro GET_IP, reg
703||	if (GCC_GLOBAL_REGS) {
704|		mov reg, IP
705||	} else {
706|		ldr reg, EX->opline
707||	}
708|.endmacro
709
710/* Update IP with register 'reg'. Note: shift variant is handled by ADD_IP_SHIFT. */
711|.macro ADD_IP, reg, tmp_reg
712||	if (GCC_GLOBAL_REGS) {
713|		add IP, IP, reg
714||	} else {
715|		ldr tmp_reg, EX->opline
716|		add tmp_reg, tmp_reg, reg
717|		str tmp_reg, EX->opline
718||	}
719|.endmacro
720
721|.macro ADD_IP_SHIFT, reg, shift, tmp_reg
722||	if (GCC_GLOBAL_REGS) {
723|		add IP, IP, reg, shift
724||	} else {
725|		ldr tmp_reg, EX->opline
726|		add tmp_reg, tmp_reg, reg, shift
727|		str tmp_reg, EX->opline
728||	}
729|.endmacro
730
731/* Update IP with 32-bit immediate 'val'. */
732|.macro ADD_IP_WITH_CONST, val, tmp_reg
733||	ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(val)));
734||	if (GCC_GLOBAL_REGS) {
735|		add IP, IP, #val
736||	} else {
737|		ldr tmp_reg, EX->opline
738|		add tmp_reg, tmp_reg, #val
739|		str tmp_reg, EX->opline
740||	}
741|.endmacro
742
743|.macro JMP_IP, tmp_reg
744||	if (GCC_GLOBAL_REGS) {
745|		ldr tmp_reg, [IP]
746|		br tmp_reg
747||	} else {
748|		ldr tmp_reg, EX:CARG1->opline
749|		br tmp_reg
750||	}
751|.endmacro
752
753|.macro CMP_IP, addr, tmp_reg1, tmp_reg2
754|	LOAD_ADDR tmp_reg1, addr
755||	if (GCC_GLOBAL_REGS) {
756|		cmp IP, tmp_reg1
757||	} else {
758|		ldr tmp_reg2, EX->opline
759|		cmp tmp_reg2, tmp_reg1
760||	}
761|.endmacro
762
763|.macro LOAD_ZVAL_ADDR, reg, addr
764||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
765|		LOAD_ADDR reg, Z_ZV(addr)
766||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
767||		if (Z_OFFSET(addr)) {
768|			ADD_SUB_64_WITH_CONST_32 add, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), reg
769||		} else {
770||			if (Z_REG(addr) == ZREG_RSP) {
771|				mov reg, sp
772||			} else {
773|				mov reg, Rx(Z_REG(addr))
774||			}
775||		}
776||	} else {
777||		ZEND_UNREACHABLE();
778||	}
779|.endmacro
780
781|.macro GET_Z_TYPE_INFO, reg, zv
782|	ldr reg, [zv, #offsetof(zval,u1.type_info)]
783|.endmacro
784
785|.macro SET_Z_TYPE_INFO, zv, type, tmp_reg
786|	LOAD_32BIT_VAL tmp_reg, type
787|	str tmp_reg, [zv, #offsetof(zval,u1.type_info)]
788|.endmacro
789
790|.macro GET_ZVAL_TYPE, reg, addr, tmp_reg
791||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
792|	MEM_ACCESS_8_WITH_UOFFSET ldrb, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.v.type), tmp_reg
793|.endmacro
794
795|.macro GET_ZVAL_TYPE_INFO, reg, addr, tmp_reg
796||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
797|	MEM_ACCESS_32_WITH_UOFFSET ldr, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg
798|.endmacro
799
800|.macro SET_ZVAL_TYPE_INFO, addr, type, tmp_reg1, tmp_reg2
801||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
802|	LOAD_32BIT_VAL tmp_reg1, type
803|	MEM_ACCESS_32_WITH_UOFFSET str, tmp_reg1, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg2
804|.endmacro
805
806|.macro SET_ZVAL_TYPE_INFO_FROM_REG, addr, reg, tmp_reg
807||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
808|	MEM_ACCESS_32_WITH_UOFFSET str, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg
809|.endmacro
810
811|.macro GET_Z_PTR, reg, zv
812|	ldr reg, [zv]
813|.endmacro
814
815|.macro GET_ZVAL_PTR, reg, addr, tmp_reg
816||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
817|	MEM_ACCESS_64_WITH_UOFFSET ldr, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
818|.endmacro
819
820|.macro SET_ZVAL_PTR, addr, reg, tmp_reg
821||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
822|	MEM_ACCESS_64_WITH_UOFFSET str, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
823|.endmacro
824
825|.macro UNDEF_OPLINE_RESULT, tmp_reg
826|	ldr REG0, EX->opline
827|	ldr REG0w, OP:REG0->result.var
828|	add REG0, FP, REG0
829|	SET_Z_TYPE_INFO REG0, IS_UNDEF, tmp_reg
830|.endmacro
831
832|.macro UNDEF_OPLINE_RESULT_IF_USED, tmp_reg1, tmp_reg2
833|	ldrb tmp_reg1, OP:RX->result_type
834|	TST_32_WITH_CONST tmp_reg1, (IS_TMP_VAR|IS_VAR), tmp_reg2
835|	beq >1
836|	ldr REG0w, OP:RX->result.var
837|	add REG0, FP, REG0
838|	SET_Z_TYPE_INFO REG0, IS_UNDEF, tmp_reg1
839|1:
840|.endmacro
841
842/* Floating-point comparison between register 'reg' and value from memory 'addr'.
843 * Note: the equivalent macros in JIT/x86 are SSE_AVX_OP and SSE_OP. */
844|.macro DOUBLE_CMP, reg, addr, tmp_reg, fp_tmp_reg
845||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
846|		MEM_LOAD ldr, Rd(fp_tmp_reg-ZREG_V0), Z_ZV(addr), Rx(tmp_reg)
847|		fcmp Rd(reg-ZREG_V0), Rd(fp_tmp_reg-ZREG_V0)
848||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
849|		MEM_ACCESS_64_WITH_UOFFSET ldr, Rd(fp_tmp_reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg)
850|		fcmp Rd(reg-ZREG_V0), Rd(fp_tmp_reg-ZREG_V0)
851||	} else if (Z_MODE(addr) == IS_REG) {
852|		fcmp Rd(reg-ZREG_V0), Rd(Z_REG(addr)-ZREG_V0)
853||	} else {
854||		ZEND_UNREACHABLE();
855||	}
856|.endmacro
857
858/* Convert LONG value 'val' into DOUBLE type, and move it into FP register 'reg'.
859 * Note: the equivalent macro in JIT/x86 is SSE_GET_LONG. */
860|.macro DOUBLE_GET_LONG, reg, val, tmp_reg
861||	if (val == 0) {
862|		fmov Rd(reg-ZREG_V0), xzr  // TODO: "movi d0, #0" is not recognized by DynASM/arm64
863||	} else {
864|		LOAD_64BIT_VAL Rx(tmp_reg), val
865|		scvtf Rd(reg-ZREG_V0), Rx(tmp_reg)
866||	}
867|.endmacro
868
869/* Convert LONG value from memory 'addr' into DOUBLE type, and move it into FP register 'reg'.
870 * Note: the equivalent macro in JIT/x86 is SSE_GET_ZVAL_LVAL. */
871|.macro DOUBLE_GET_ZVAL_LVAL, reg, addr, tmp_reg1, tmp_reg2
872||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
873|		DOUBLE_GET_LONG reg, Z_LVAL_P(Z_ZV(addr)), tmp_reg1
874||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
875|		MEM_ACCESS_64_WITH_UOFFSET ldr, Rx(tmp_reg1), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg2)
876|		scvtf Rd(reg-ZREG_V0), Rx(tmp_reg1)
877||	} else if (Z_MODE(addr) == IS_REG) {
878|		scvtf Rd(reg-ZREG_V0), Rx(Z_REG(addr))
879||	} else {
880||		ZEND_UNREACHABLE();
881||	}
882|.endmacro
883
884/* Floating-point arithmetic operation between two FP registers.
885 * Note: the equivalent macro in JIT/x86 is AVX_MATH_REG. */
886|.macro DOUBLE_MATH_REG, opcode, dst_reg, op1_reg, op2_reg
887||	switch (opcode) {
888||		case ZEND_ADD:
889|			fadd Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
890||			break;
891||		case ZEND_SUB:
892|			fsub Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
893||			break;
894||		case ZEND_MUL:
895|			fmul Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
896||			break;
897||		case ZEND_DIV:
898|			fdiv Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0)
899||			break;
900||	}
901|.endmacro
902
903/* Conduct binary operation between register 'reg' and value from memory 'addr',
904 * and the computation result is stored in 'reg'.
905 * For LONG_ADD_SUB, 'add_sub_ins' can be adds/subs. For LONG_BW_OP, 'bw_ins' can be and/orr/eor.
906 * For LONG_CMP, 'cmp' instruction is used by default and only flag registers are affected.
907 * Note: the equivalent macro in JIT/x86 is LONG_OP. */
908|.macro LONG_ADD_SUB, add_sub_ins, reg, addr, tmp_reg
909||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
910|		ADD_SUB_64_WITH_CONST add_sub_ins, Rx(reg), Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg
911||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
912|		MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
913|		add_sub_ins Rx(reg), Rx(reg), tmp_reg
914||	} else if (Z_MODE(addr) == IS_REG) {
915|		add_sub_ins Rx(reg), Rx(reg), Rx(Z_REG(addr))
916||	} else {
917||		ZEND_UNREACHABLE();
918||	}
919|.endmacro
920
921|.macro LONG_BW_OP, bw_ins, reg, addr, tmp_reg
922||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
923|		BW_OP_64_WITH_CONST bw_ins, Rx(reg), Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg
924||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
925|		MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
926|		bw_ins Rx(reg), Rx(reg), tmp_reg
927||	} else if (Z_MODE(addr) == IS_REG) {
928|		bw_ins Rx(reg), Rx(reg), Rx(Z_REG(addr))
929||	} else {
930||		ZEND_UNREACHABLE();
931||	}
932|.endmacro
933
934|.macro LONG_CMP, reg, addr, tmp_reg
935||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
936|		CMP_64_WITH_CONST Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg
937||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
938|		MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
939|		cmp Rx(reg), tmp_reg
940||	} else if (Z_MODE(addr) == IS_REG) {
941|		cmp Rx(reg), Rx(Z_REG(addr))
942||	} else {
943||		ZEND_UNREACHABLE();
944||	}
945|.endmacro
946
947/* Conduct add/sub between value from memory 'addr' and an immediate value 'val', and
948 * the computation result is stored back into 'addr'.
949 * Note: it should be guaranteed that 'val' can be encoded into add/sub instruction. */
950|.macro LONG_ADD_SUB_WITH_IMM, add_sub_ins, addr, val, tmp_reg1, tmp_reg2
951||	ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(val)));
952||	if (Z_MODE(addr) == IS_MEM_ZVAL) {
953||		if (((uint32_t)(Z_OFFSET(addr))) > LDR_STR_PIMM64) {
954|			LOAD_32BIT_VAL tmp_reg2, Z_OFFSET(addr)
955|			ldr tmp_reg1, [Rx(Z_REG(addr)), tmp_reg2]
956|			add_sub_ins tmp_reg1, tmp_reg1, #val
957|			str tmp_reg1, [Rx(Z_REG(addr)), tmp_reg2]
958||		} else {
959|			ldr tmp_reg1, [Rx(Z_REG(addr)), #Z_OFFSET(addr)]
960|			add_sub_ins tmp_reg1, tmp_reg1, #val
961|			str tmp_reg1, [Rx(Z_REG(addr)), #Z_OFFSET(addr)]
962||		}
963||	} else if (Z_MODE(addr) == IS_REG) {
964|		add_sub_ins Rx(Z_REG(addr)), Rx(Z_REG(addr)), #val
965||	} else {
966||		ZEND_UNREACHABLE();
967||	}
968|.endmacro
969
970/* Compare value from memory 'addr' with immediate value 'val'.
971 * Note: the equivalent macro in JIT/x86 is LONG_OP_WITH_CONST. */
972|.macro LONG_CMP_WITH_CONST, addr, val, tmp_reg1, tmp_reg2
973||	if (Z_MODE(addr) == IS_MEM_ZVAL) {
974|		MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg1, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg2
975|		CMP_64_WITH_CONST tmp_reg1, val, tmp_reg2
976||	} else if (Z_MODE(addr) == IS_REG) {
977|		CMP_64_WITH_CONST Rx(Z_REG(addr)), val, tmp_reg1
978||	} else {
979||		ZEND_UNREACHABLE();
980||	}
981|.endmacro
982
983|.macro GET_ZVAL_LVAL, reg, addr, tmp_reg
984||	if (Z_MODE(addr) == IS_CONST_ZVAL) {
985||		if (Z_LVAL_P(Z_ZV(addr)) == 0) {
986|			mov Rx(reg), xzr
987||		} else {
988|			LOAD_64BIT_VAL Rx(reg), Z_LVAL_P(Z_ZV(addr))
989||		}
990||	} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
991|		MEM_ACCESS_64_WITH_UOFFSET ldr, Rx(reg), Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
992||	} else if (Z_MODE(addr) == IS_REG) {
993||		if (reg != Z_REG(addr)) {
994|			mov Rx(reg), Rx(Z_REG(addr))
995||		}
996||	} else {
997||		ZEND_UNREACHABLE();
998||	}
999|.endmacro
1000
1001|.macro LONG_MATH, opcode, reg, addr, tmp_reg1
1002||	switch (opcode) {
1003||		case ZEND_ADD:
1004|			LONG_ADD_SUB adds, reg, addr, tmp_reg1
1005||			break;
1006||		case ZEND_SUB:
1007|			LONG_ADD_SUB subs, reg, addr, tmp_reg1
1008||			break;
1009||		case ZEND_BW_OR:
1010|			LONG_BW_OP orr, reg, addr, tmp_reg1
1011||			break;
1012||		case ZEND_BW_AND:
1013|			LONG_BW_OP and, reg, addr, tmp_reg1
1014||			break;
1015||		case ZEND_BW_XOR:
1016|			LONG_BW_OP eor, reg, addr, tmp_reg1
1017||			break;
1018||		default:
1019||			ZEND_UNREACHABLE();
1020||	}
1021|.endmacro
1022
1023|.macro LONG_MATH_REG, opcode, dst_reg, src_reg1, src_reg2
1024||	switch (opcode) {
1025||		case ZEND_ADD:
1026|			adds dst_reg, src_reg1, src_reg2
1027||			break;
1028||		case ZEND_SUB:
1029|			subs dst_reg, src_reg1, src_reg2
1030||			break;
1031||		case ZEND_BW_OR:
1032|			orr dst_reg, src_reg1, src_reg2
1033||			break;
1034||		case ZEND_BW_AND:
1035|			and dst_reg, src_reg1, src_reg2
1036||			break;
1037||		case ZEND_BW_XOR:
1038|			eor dst_reg, src_reg1, src_reg2
1039||			break;
1040||		default:
1041||			ZEND_UNREACHABLE();
1042||	}
1043|.endmacro
1044
1045/* Store LONG value into memory 'addr'.
1046 * This LONG value can be an immediate value i.e. 'val' in macro SET_ZVAL_LVAL, or
1047 * a register value i.e. 'reg' in macro SET_ZVAL_LVAL_FROM_REG. */
1048|.macro SET_ZVAL_LVAL_FROM_REG, addr, reg, tmp_reg
1049||	if (Z_MODE(addr) == IS_REG) {
1050|		mov Rx(Z_REG(addr)), reg
1051||	} else {
1052||		ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1053|		MEM_ACCESS_64_WITH_UOFFSET str, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg
1054||	}
1055|.endmacro
1056
1057|.macro SET_ZVAL_LVAL, addr, val, tmp_reg1, tmp_reg2
1058||	if (val == 0) {
1059|		SET_ZVAL_LVAL_FROM_REG addr, xzr, tmp_reg2
1060||	} else {
1061|		LOAD_64BIT_VAL tmp_reg1, val
1062|		SET_ZVAL_LVAL_FROM_REG addr, tmp_reg1, tmp_reg2
1063||	}
1064|.endmacro
1065
1066/* Store DOUBLE value from FP register 'reg' into memory 'addr'.
1067 * Note: the equivalent macro in JIT/x86 is SSE_SET_ZVAL_DVAL. */
1068|.macro SET_ZVAL_DVAL, addr, reg, tmp_reg
1069||	if (Z_MODE(addr) == IS_REG) {
1070||		if (reg != Z_REG(addr)) {
1071|			fmov Rd(Z_REG(addr)-ZREG_V0), Rd(reg-ZREG_V0)
1072||		}
1073||	} else {
1074||		ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1075|		MEM_ACCESS_64_WITH_UOFFSET str, Rd(reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg)
1076||	}
1077|.endmacro
1078
1079/* Load DOUBLE value from memory 'addr' into FP register 'reg'.
1080 * Note: the equivalent macro in JIT/x86 is SSE_GET_ZVAL_DVAL. */
1081|.macro GET_ZVAL_DVAL, reg, addr, tmp_reg
1082||	if (Z_MODE(addr) != IS_REG || reg != Z_REG(addr)) {
1083||		if (Z_MODE(addr) == IS_CONST_ZVAL) {
1084|			MEM_LOAD ldr, Rd(reg-ZREG_V0), Z_ZV(addr), Rx(tmp_reg)
1085||		} else if (Z_MODE(addr) == IS_MEM_ZVAL) {
1086|			MEM_ACCESS_64_WITH_UOFFSET ldr, Rd(reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg)
1087||		} else if (Z_MODE(addr) == IS_REG) {
1088|			fmov Rd(reg-ZREG_V0), Rd(Z_REG(addr)-ZREG_V0)
1089||		} else {
1090||			ZEND_UNREACHABLE();
1091||		}
1092||	}
1093|.endmacro
1094
1095|.macro ZVAL_COPY_CONST, dst_addr, dst_info, dst_def_info, zv, tmp_reg1, tmp_reg2, fp_tmp_reg
1096||	if (Z_TYPE_P(zv) > IS_TRUE) {
1097||		if (Z_TYPE_P(zv) == IS_DOUBLE) {
1098||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : fp_tmp_reg;
1099|			MEM_LOAD ldr, Rd(dst_reg-ZREG_V0), zv, Rx(tmp_reg1)
1100|			SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2
1101||		} else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) {
1102||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : fp_tmp_reg;
1103|			DOUBLE_GET_LONG dst_reg, Z_LVAL_P(zv), tmp_reg1
1104|			SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2
1105||		} else {
1106|			// In x64, if the range of this LONG value can be represented via INT type, only move the low 32 bits into dst_addr.
1107|			// Note that imm32 is signed extended to 64 bits during mov.
1108|			// In aarch64, we choose to handle both cases in the same way. Even though 4 mov's are used for 64-bit value and 2 mov's are
1109|			// needed for 32-bit value, an extra ext insn is needed for 32-bit vlaue.
1110|			SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
1111||		}
1112||	}
1113||	if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
1114||		if (dst_def_info == MAY_BE_DOUBLE) {
1115||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
1116|				SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2)
1117||			}
1118||		} 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) {
1119|			SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2)
1120||		}
1121||	}
1122|.endmacro
1123
1124|.macro ZVAL_COPY_CONST_2, dst_addr, res_addr, dst_info, dst_def_info, zv, tmp_reg1, tmp_reg2, fp_tmp_reg
1125||	if (Z_TYPE_P(zv) > IS_TRUE) {
1126||		if (Z_TYPE_P(zv) == IS_DOUBLE) {
1127||			zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ?
1128||				Z_REG(dst_addr) : ((Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : fp_tmp_reg);
1129|			MEM_LOAD ldr, Rd(dst_reg-ZREG_V0), zv, Rx(tmp_reg1)
1130|			SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2
1131|			SET_ZVAL_DVAL res_addr, dst_reg, tmp_reg2
1132||		} else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) {
1133||			if (Z_MODE(dst_addr) == IS_REG) {
1134|				DOUBLE_GET_LONG Z_REG(dst_addr), Z_LVAL_P(zv), tmp_reg1
1135|				SET_ZVAL_DVAL res_addr, Z_REG(dst_addr), tmp_reg2
1136||			} else if (Z_MODE(res_addr) == IS_REG) {
1137|				DOUBLE_GET_LONG Z_REG(res_addr), Z_LVAL_P(zv), tmp_reg1
1138|				SET_ZVAL_DVAL dst_addr, Z_REG(res_addr), tmp_reg2
1139||			} else {
1140|				DOUBLE_GET_LONG fp_tmp_reg, Z_LVAL_P(zv), tmp_reg1
1141|				SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg2
1142|				SET_ZVAL_DVAL res_addr, fp_tmp_reg, tmp_reg2
1143||			}
1144||		} else {
1145||			if (Z_MODE(dst_addr) == IS_REG) {
1146|				SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
1147|				SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(dst_addr)), Rx(tmp_reg1)
1148||			} else if (Z_MODE(res_addr) == IS_REG) {
1149|				SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
1150|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(res_addr)), Rx(tmp_reg1)
1151||			} else {
1152|				SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
1153|				SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2)
1154||			}
1155||		}
1156||	}
1157||	if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
1158||		if (dst_def_info == MAY_BE_DOUBLE) {
1159||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
1160|				SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2)
1161||			}
1162||		} 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) {
1163|			SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2)
1164||		}
1165||	}
1166||	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
1167||		if (dst_def_info == MAY_BE_DOUBLE) {
1168|			SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2)
1169||		} else {
1170|			SET_ZVAL_TYPE_INFO res_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2)
1171||		}
1172||	}
1173|.endmacro
1174
1175// the same as above, but "src" may overlap with "reg1"
1176// Useful info would be stored into reg1 and reg2, and they might be used afterward.
1177|.macro ZVAL_COPY_VALUE, dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg1, tmp_reg2, fp_tmp_reg
1178|	ZVAL_COPY_VALUE_V dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg1, fp_tmp_reg
1179||	if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) &&
1180||		!(src_info & MAY_BE_GUARD) &&
1181||		has_concrete_type(src_info & MAY_BE_ANY)) {
1182||		if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
1183||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD))) {
1184||				uint32_t type = concrete_type(src_info);
1185|				SET_ZVAL_TYPE_INFO dst_addr, type, Rw(tmp_reg1), Rx(tmp_reg2)
1186||			}
1187||		}
1188||	} else {
1189|		GET_ZVAL_TYPE_INFO Rw(reg1), src_addr, Rx(tmp_reg1)
1190|		SET_ZVAL_TYPE_INFO_FROM_REG dst_addr, Rw(reg1), Rx(tmp_reg1)
1191||	}
1192|.endmacro
1193
1194|.macro ZVAL_COPY_VALUE_V, dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg, fp_tmp_reg
1195||	if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
1196||		if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_LONG) {
1197||			if (Z_MODE(src_addr) == IS_REG) {
1198||				if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) {
1199|					SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg)
1200||				}
1201||			} else if (Z_MODE(dst_addr) == IS_REG) {
1202|				GET_ZVAL_LVAL Z_REG(dst_addr), src_addr, Rx(tmp_reg)
1203||			} else {
1204|				GET_ZVAL_LVAL reg2, src_addr, Rx(tmp_reg)
1205|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(reg2), Rx(tmp_reg)
1206||			}
1207||		} else if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
1208||			if (Z_MODE(src_addr) == IS_REG) {
1209|				SET_ZVAL_DVAL dst_addr, Z_REG(src_addr), tmp_reg
1210||			} else if (Z_MODE(dst_addr) == IS_REG) {
1211|				GET_ZVAL_DVAL Z_REG(dst_addr), src_addr, tmp_reg
1212||			} else {
1213|				GET_ZVAL_DVAL fp_tmp_reg, src_addr, tmp_reg
1214|				SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg
1215||			}
1216||		// Combine the following two branches.
1217||		// } else if (!(src_info & (MAY_BE_DOUBLE|MAY_BE_GUARD))) {
1218||		} else {
1219|			GET_ZVAL_PTR Rx(reg2), src_addr, Rx(tmp_reg)
1220|			SET_ZVAL_PTR dst_addr, Rx(reg2), Rx(tmp_reg)
1221||		}
1222||	}
1223|.endmacro
1224
1225|.macro ZVAL_COPY_VALUE_2, dst_addr, dst_info, res_addr, src_addr, src_info, reg1, reg2, tmp_reg, tmp_reg2, fp_tmp_reg
1226||	if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
1227||		if ((src_info & MAY_BE_ANY) == MAY_BE_LONG) {
1228||			if (Z_MODE(src_addr) == IS_REG) {
1229||				if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) {
1230|					SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg)
1231||				}
1232||				if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(src_addr)) {
1233|					SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg)
1234||				}
1235||			} else if (Z_MODE(dst_addr) == IS_REG) {
1236|				GET_ZVAL_LVAL Z_REG(dst_addr), src_addr, Rx(tmp_reg)
1237||				if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(dst_addr)) {
1238|					SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(dst_addr)), Rx(tmp_reg)
1239||				}
1240||			} else if (Z_MODE(res_addr) == IS_REG) {
1241|				GET_ZVAL_LVAL Z_REG(res_addr), src_addr, Rx(tmp_reg)
1242|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(res_addr)), Rx(tmp_reg)
1243||			} else {
1244|				GET_ZVAL_LVAL reg2, src_addr, Rx(tmp_reg)
1245|				SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(reg2), Rx(tmp_reg)
1246|				SET_ZVAL_LVAL_FROM_REG res_addr, Rx(reg2), Rx(tmp_reg)
1247||			}
1248||		} else if ((src_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
1249||			if (Z_MODE(src_addr) == IS_REG) {
1250|				SET_ZVAL_DVAL dst_addr, Z_REG(src_addr), tmp_reg
1251|				SET_ZVAL_DVAL res_addr, Z_REG(src_addr), tmp_reg
1252||			} else if (Z_MODE(dst_addr) == IS_REG) {
1253|				GET_ZVAL_DVAL Z_REG(dst_addr), src_addr, tmp_reg
1254|				SET_ZVAL_DVAL res_addr, Z_REG(dst_addr), tmp_reg
1255||			} else if (Z_MODE(res_addr) == IS_REG) {
1256|				GET_ZVAL_DVAL Z_REG(res_addr), src_addr, tmp_reg
1257|				SET_ZVAL_DVAL dst_addr, Z_REG(res_addr), tmp_reg
1258||			} else {
1259|				GET_ZVAL_DVAL fp_tmp_reg, src_addr, tmp_reg
1260|				SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg
1261|				SET_ZVAL_DVAL res_addr, fp_tmp_reg, tmp_reg
1262||			}
1263||		} else {
1264|			GET_ZVAL_PTR Rx(reg2), src_addr, Rx(tmp_reg)
1265|			SET_ZVAL_PTR dst_addr, Rx(reg2), Rx(tmp_reg)
1266|			SET_ZVAL_PTR res_addr, Rx(reg2), Rx(tmp_reg)
1267||		}
1268||	}
1269||	if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) &&
1270||	    has_concrete_type(src_info & MAY_BE_ANY)) {
1271||		uint32_t type = concrete_type(src_info);
1272||		if (Z_MODE(dst_addr) == IS_MEM_ZVAL) {
1273||			if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF))) {
1274|				SET_ZVAL_TYPE_INFO dst_addr, type, Rw(tmp_reg), Rx(tmp_reg2)
1275||			}
1276||		}
1277||		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
1278|			SET_ZVAL_TYPE_INFO res_addr, type, Rw(tmp_reg), Rx(tmp_reg2)
1279||		}
1280||	} else {
1281|		GET_ZVAL_TYPE_INFO Rw(reg1), src_addr, Rx(tmp_reg)
1282|		SET_ZVAL_TYPE_INFO_FROM_REG dst_addr, Rw(reg1), Rx(tmp_reg)
1283|		SET_ZVAL_TYPE_INFO_FROM_REG res_addr, Rw(reg1), Rx(tmp_reg)
1284||	}
1285|.endmacro
1286
1287|.macro IF_UNDEF, type_reg, label
1288|	cbz type_reg, label
1289|.endmacro
1290
1291|.macro IF_TYPE, type, val, label
1292||	if (val == 0) {
1293|		cbz type, label
1294||	} else {
1295|		cmp type, #val
1296|		beq label
1297||	}
1298|.endmacro
1299
1300|.macro IF_NOT_TYPE, type, val, label
1301||	if (val == 0) {
1302|		cbnz type, label
1303||	} else {
1304|		cmp type, #val
1305|		bne label
1306||	}
1307|.endmacro
1308
1309|.macro IF_Z_TYPE, zv, val, label, tmp_reg
1310|	ldrb tmp_reg, [zv, #offsetof(zval, u1.v.type)]
1311|	IF_TYPE tmp_reg, val, label
1312|.endmacro
1313
1314|.macro IF_NOT_Z_TYPE, zv, val, label, tmp_reg
1315|	ldrb tmp_reg, [zv, #offsetof(zval, u1.v.type)]
1316|	IF_NOT_TYPE tmp_reg, val, label
1317|.endmacro
1318
1319|.macro CMP_ZVAL_TYPE, addr, val, tmp_reg
1320||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1321|	MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg)
1322|	cmp Rw(tmp_reg), #val
1323|.endmacro
1324
1325|.macro IF_ZVAL_TYPE, addr, val, label, tmp_reg
1326||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1327|	MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg)
1328|	IF_TYPE Rw(tmp_reg), val, label
1329|.endmacro
1330
1331|.macro IF_NOT_ZVAL_TYPE, addr, val, label, tmp_reg
1332||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1333|	MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg)
1334|	IF_NOT_TYPE Rw(tmp_reg), val, label
1335|.endmacro
1336
1337|.macro IF_FLAGS, type_flags, mask, label, tmp_reg
1338|	TST_32_WITH_CONST type_flags, mask, tmp_reg
1339|	bne label
1340|.endmacro
1341
1342|.macro IF_NOT_FLAGS, type_flags, mask, label, tmp_reg
1343|	TST_32_WITH_CONST type_flags, mask, tmp_reg
1344|	beq label
1345|.endmacro
1346
1347|.macro IF_REFCOUNTED, type_flags, label, tmp_reg
1348|	TST_32_WITH_CONST type_flags, (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), tmp_reg
1349|	bne label
1350|.endmacro
1351
1352|.macro IF_NOT_REFCOUNTED, type_flags, label, tmp_reg
1353|	TST_32_WITH_CONST type_flags, (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), tmp_reg
1354|	beq label
1355|.endmacro
1356
1357|.macro IF_ZVAL_FLAGS, addr, mask, label, tmp_reg1, tmp_reg2
1358||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1359|	MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg1), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type_flags), Rx(tmp_reg2)
1360|	IF_FLAGS Rw(tmp_reg1), mask, label, Rw(tmp_reg2)
1361|.endmacro
1362
1363|.macro IF_NOT_ZVAL_FLAGS, addr, mask, label, tmp_reg1, tmp_reg2
1364||	ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL);
1365|	MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg1), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type_flags), Rx(tmp_reg2)
1366|	IF_NOT_FLAGS Rw(tmp_reg1), mask, label, Rw(tmp_reg2)
1367|.endmacro
1368
1369|.macro IF_ZVAL_REFCOUNTED, addr, label, tmp_reg1, tmp_reg2
1370|	IF_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label, tmp_reg1, tmp_reg2
1371|.endmacro
1372
1373|.macro IF_NOT_ZVAL_REFCOUNTED, addr, label, tmp_reg1, tmp_reg2
1374|	IF_NOT_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label, tmp_reg1, tmp_reg2
1375|.endmacro
1376
1377|.macro IF_NOT_ZVAL_COLLECTABLE, addr, label, tmp_reg1, tmp_reg2
1378|	IF_NOT_ZVAL_FLAGS addr, IS_TYPE_COLLECTABLE, label, tmp_reg1, tmp_reg2
1379|.endmacro
1380
1381|.macro GC_ADDREF, zv, tmp_reg
1382|	ldr tmp_reg, [zv]
1383|	add tmp_reg, tmp_reg, #1
1384|	str tmp_reg, [zv]
1385|.endmacro
1386
1387|.macro GC_ADDREF_2, zv, tmp_reg
1388|	ldr tmp_reg, [zv]
1389|	add tmp_reg, tmp_reg, #2
1390|	str tmp_reg, [zv]
1391|.endmacro
1392
1393|.macro GC_DELREF, zv, tmp_reg
1394|	ldr tmp_reg, [zv]
1395|	subs tmp_reg, tmp_reg, #1
1396|	str tmp_reg, [zv]
1397|.endmacro
1398
1399|.macro IF_GC_MAY_NOT_LEAK, ptr, label, tmp_reg1, tmp_reg2
1400|	ldr tmp_reg1, [ptr, #4]
1401|	TST_32_WITH_CONST tmp_reg1, (GC_INFO_MASK | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)), tmp_reg2
1402|	bne label
1403|.endmacro
1404
1405|.macro ADDREF_CONST, zv, tmp_reg1, tmp_reg2
1406|	LOAD_ADDR tmp_reg1, Z_PTR_P(zv)
1407|	ldr tmp_reg2, [tmp_reg1]
1408|	add tmp_reg2, tmp_reg2, #1
1409|	str tmp_reg2, [tmp_reg1]
1410|.endmacro
1411
1412|.macro ADDREF_CONST_2, zv, tmp_reg1, tmp_reg2
1413|	LOAD_ADDR tmp_reg1, Z_PTR_P(zv)
1414|	ldr tmp_reg2, [tmp_reg1]
1415|	add tmp_reg2, tmp_reg2, #2
1416|	str tmp_reg2, [tmp_reg1]
1417|.endmacro
1418
1419|.macro TRY_ADDREF, val_info, type_flags_reg, value_ptr_reg, tmp_reg
1420||	if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
1421||		if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1422|			IF_NOT_REFCOUNTED type_flags_reg, >1, tmp_reg
1423||		}
1424|		GC_ADDREF value_ptr_reg, tmp_reg
1425|1:
1426||	}
1427|.endmacro
1428
1429|.macro TRY_ADDREF_2, val_info, type_flags_reg, value_ptr_reg, tmp_reg
1430||	if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
1431||		if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1432|			IF_NOT_REFCOUNTED type_flags_reg, >1, tmp_reg
1433||		}
1434|		ldr tmp_reg, [value_ptr_reg]
1435|		add tmp_reg, tmp_reg, #2
1436|		str tmp_reg, [value_ptr_reg]
1437|1:
1438||	}
1439|.endmacro
1440
1441|.macro ZVAL_DEREF, reg, info, tmp_reg
1442||	if (info & MAY_BE_REF) {
1443|		IF_NOT_Z_TYPE, reg, IS_REFERENCE, >1, tmp_reg
1444|		GET_Z_PTR reg, reg
1445|		add reg, reg, #offsetof(zend_reference, val)
1446|1:
1447||	}
1448|.endmacro
1449
1450|.macro SET_EX_OPLINE, op, tmp_reg
1451||	if (op == last_valid_opline) {
1452||		zend_jit_use_last_valid_opline();
1453|		SAVE_IP
1454||	} else {
1455|		ADDR_STORE EX->opline, op, tmp_reg
1456||		if (!GCC_GLOBAL_REGS) {
1457||			zend_jit_reset_last_valid_opline();
1458||		}
1459||	}
1460|.endmacro
1461
1462// arg1 "zval" should be in FCARG1x
1463|.macro ZVAL_DTOR_FUNC, var_info, opline, tmp_reg
1464||	do {
1465||		if (!((var_info) & MAY_BE_GUARD)
1466||		 && has_concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1467||			zend_uchar type = concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
1468||			if (type == IS_STRING && !ZEND_DEBUG) {
1469|				EXT_CALL _efree, tmp_reg
1470||				break;
1471||			} else if (type == IS_ARRAY) {
1472||				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)) {
1473||					if (opline && ((var_info) & (MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_REF))) {
1474|						SET_EX_OPLINE opline, tmp_reg
1475||					}
1476|					EXT_CALL zend_array_destroy, tmp_reg
1477||				} else {
1478|					EXT_CALL zend_jit_array_free, tmp_reg
1479||				}
1480||				break;
1481||			} else if (type == IS_OBJECT) {
1482||				if (opline) {
1483|					SET_EX_OPLINE opline, REG0
1484||				}
1485|				EXT_CALL zend_objects_store_del, tmp_reg
1486||				break;
1487||			}
1488||		}
1489||		if (opline) {
1490|			SET_EX_OPLINE opline, tmp_reg
1491||		}
1492|		EXT_CALL rc_dtor_func, tmp_reg
1493||	} while(0);
1494|.endmacro
1495
1496|.macro ZVAL_PTR_DTOR, addr, op_info, gc, cold, opline, tmp_reg1, tmp_reg2
1497||	if ((op_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF|MAY_BE_GUARD)) {
1498||		if ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1499|			// if (Z_REFCOUNTED_P(cv)) {
1500||			if (cold) {
1501|				IF_ZVAL_REFCOUNTED addr, >1, tmp_reg1, tmp_reg2
1502|.cold_code
1503|1:
1504||			} else {
1505|				IF_NOT_ZVAL_REFCOUNTED addr, >4, tmp_reg1, tmp_reg2
1506||			}
1507||		}
1508|		// if (!Z_DELREF_P(cv)) {
1509|		GET_ZVAL_PTR FCARG1x, addr, Rx(tmp_reg2)
1510|		GC_DELREF FCARG1x, Rw(tmp_reg1)
1511||		if (((op_info) & MAY_BE_GUARD) || RC_MAY_BE_1(op_info)) {
1512||			if (((op_info) & MAY_BE_GUARD) || RC_MAY_BE_N(op_info)) {
1513||				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))) {
1514|					bne >3
1515||				} else {
1516|					bne >4
1517||				}
1518||			}
1519|			// zval_dtor_func(r);
1520|			ZVAL_DTOR_FUNC op_info, opline, Rx(tmp_reg1)
1521||			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))) {
1522|				b >4
1523||			}
1524|3:
1525||		}
1526||		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))) {
1527||			if ((op_info) & (MAY_BE_REF|MAY_BE_GUARD)) {
1528||				zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, offsetof(zend_reference, val));
1529|				IF_NOT_ZVAL_TYPE addr, IS_REFERENCE, >1, tmp_reg1
1530|				IF_NOT_ZVAL_COLLECTABLE ref_addr, >4, tmp_reg1, tmp_reg2
1531|				GET_ZVAL_PTR FCARG1x, ref_addr, Rx(tmp_reg2)
1532|1:
1533||			}
1534|			IF_GC_MAY_NOT_LEAK FCARG1x, >4, Rw(tmp_reg1), Rw(tmp_reg2)
1535|			// gc_possible_root(Z_COUNTED_P(z))
1536|			EXT_CALL gc_possible_root, Rx(tmp_reg1)
1537||		}
1538||		if (cold && ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) != 0) {
1539|			b >4
1540|.code
1541||		}
1542|4:
1543||	}
1544|.endmacro
1545
1546|.macro FREE_OP, op_type, op, op_info, cold, opline, tmp_reg1, tmp_reg2
1547||	if (op_type & (IS_VAR|IS_TMP_VAR)) {
1548||		zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var);
1549|		ZVAL_PTR_DTOR addr, op_info, 0, cold, opline, tmp_reg1, tmp_reg2
1550||	}
1551|.endmacro
1552
1553|.macro SEPARATE_ARRAY, addr, op_info, cold, tmp_reg1, tmp_reg2
1554||	if (RC_MAY_BE_N(op_info)) {
1555||		if (Z_REG(addr) != ZREG_FP) {
1556|			GET_ZVAL_LVAL ZREG_REG0, addr, Rx(tmp_reg1)
1557||			if (RC_MAY_BE_1(op_info)) {
1558|				// if (GC_REFCOUNT() > 1)
1559|				ldr Rw(tmp_reg1), [REG0]
1560|				cmp Rw(tmp_reg1), #1
1561|				bls >2
1562||			}
1563||			if (Z_REG(addr) != ZREG_FCARG1 || Z_OFFSET(addr) != 0) {
1564|				LOAD_ZVAL_ADDR FCARG1x, addr
1565||			}
1566|			EXT_CALL zend_jit_zval_array_dup, REG0
1567|			mov REG0, RETVALx
1568|2:
1569|			mov FCARG1x, REG0
1570||		} else {
1571|			GET_ZVAL_LVAL ZREG_FCARG1, addr, Rx(tmp_reg1)
1572||			if (RC_MAY_BE_1(op_info)) {
1573|				// if (GC_REFCOUNT() > 1)
1574|				ldr Rw(tmp_reg1), [FCARG1x]
1575|				cmp Rw(tmp_reg1), #1
1576||				if (cold) {
1577|					bhi >1
1578|.cold_code
1579|1:
1580||				} else {
1581|					bls >2
1582||				}
1583||			}
1584|			IF_NOT_ZVAL_REFCOUNTED addr, >1, tmp_reg1, tmp_reg2
1585|			GC_DELREF FCARG1x, Rw(tmp_reg1)
1586|1:
1587|			EXT_CALL zend_array_dup, REG0
1588|			mov REG0, RETVALx
1589|			SET_ZVAL_PTR addr, REG0, Rx(tmp_reg1)
1590|			SET_ZVAL_TYPE_INFO addr, IS_ARRAY_EX, Rw(tmp_reg1), Rx(tmp_reg2)
1591|			mov FCARG1x, REG0
1592||			if (RC_MAY_BE_1(op_info)) {
1593||				if (cold) {
1594|					b >2
1595|.code
1596||				}
1597||			}
1598|2:
1599||		}
1600||	} else {
1601|		GET_ZVAL_LVAL ZREG_FCARG1, addr, Rx(tmp_reg1)
1602||	}
1603|.endmacro
1604
1605/* argument is passed in FCARG1x */
1606|.macro EFREE_REFERENCE
1607||#if ZEND_DEBUG
1608|		mov FCARG2x, xzr // filename
1609|		mov CARG3w, wzr  // lineno
1610|		mov CARG4, xzr
1611|		mov CARG5, xzr
1612|		EXT_CALL _efree, REG0
1613||#else
1614||#ifdef HAVE_BUILTIN_CONSTANT_P
1615|		EXT_CALL _efree_32, REG0
1616||#else
1617|		EXT_CALL _efree, REG0
1618||#endif
1619||#endif
1620|.endmacro
1621
1622|.macro EMALLOC, size, op_array, opline
1623||#if ZEND_DEBUG
1624||		const char *filename = op_array->filename ? op_array->filename->val : NULL;
1625|		mov FCARG1x, #size
1626|		LOAD_ADDR FCARG2x, filename
1627|		LOAD_32BIT_VAL CARG3w, opline->lineno
1628|		mov CARG4, xzr
1629|		mov CARG5, xzr
1630|		EXT_CALL _emalloc, REG0
1631|		mov REG0, RETVALx
1632||#else
1633||#ifdef HAVE_BUILTIN_CONSTANT_P
1634||	if (size > 24 && size <= 32) {
1635|		EXT_CALL _emalloc_32, REG0
1636|		mov REG0, RETVALx
1637||	} else {
1638|		mov FCARG1x, #size
1639|		EXT_CALL _emalloc, REG0
1640|		mov REG0, RETVALx
1641||	}
1642||#else
1643|		mov FCARG1x, #size
1644|		EXT_CALL _emalloc, REG0
1645|		mov REG0, RETVALx
1646||#endif
1647||#endif
1648|.endmacro
1649
1650|.macro OBJ_RELEASE, reg, exit_label, tmp_reg1, tmp_reg2
1651|	GC_DELREF Rx(reg), Rw(tmp_reg1)
1652|	bne >1
1653|	// zend_objects_store_del(obj);
1654||	if (reg != ZREG_FCARG1) {
1655|		mov FCARG1x, Rx(reg)
1656||	}
1657|	EXT_CALL zend_objects_store_del, Rx(tmp_reg1)
1658|	b exit_label
1659|1:
1660|	IF_GC_MAY_NOT_LEAK Rx(reg), >1, Rw(tmp_reg1), Rw(tmp_reg2)
1661|	// gc_possible_root(obj)
1662||	if (reg != ZREG_FCARG1) {
1663|		mov FCARG1x, Rx(reg)
1664||	}
1665|	EXT_CALL gc_possible_root, Rx(tmp_reg1)
1666|1:
1667|.endmacro
1668
1669|.macro UNDEFINED_OFFSET, opline
1670||	if (opline == last_valid_opline) {
1671||		zend_jit_use_last_valid_opline();
1672|		bl ->undefined_offset_ex
1673||	} else {
1674|		SET_EX_OPLINE  opline, REG0
1675|		bl ->undefined_offset
1676||	}
1677|.endmacro
1678
1679|.macro UNDEFINED_INDEX, opline
1680||	if (opline == last_valid_opline) {
1681||		zend_jit_use_last_valid_opline();
1682|		bl ->undefined_index_ex
1683||	} else {
1684|		SET_EX_OPLINE opline, REG0
1685|		bl ->undefined_index
1686||	}
1687|.endmacro
1688
1689|.macro CANNOT_ADD_ELEMENT, opline
1690||	if (opline == last_valid_opline) {
1691||		zend_jit_use_last_valid_opline();
1692|		bl ->cannot_add_element_ex
1693||	} else {
1694|		SET_EX_OPLINE opline, REG0
1695|		bl ->cannot_add_element
1696||	}
1697|.endmacro
1698
1699static bool reuse_ip = 0;
1700static bool delayed_call_chain = 0;
1701static uint32_t  delayed_call_level = 0;
1702static const zend_op *last_valid_opline = NULL;
1703static bool use_last_vald_opline = 0;
1704static bool track_last_valid_opline = 0;
1705static int jit_return_label = -1;
1706static uint32_t current_trace_num = 0;
1707static uint32_t allowed_opt_flags = 0;
1708
1709static void zend_jit_track_last_valid_opline(void)
1710{
1711	use_last_vald_opline = 0;
1712	track_last_valid_opline = 1;
1713}
1714
1715static void zend_jit_use_last_valid_opline(void)
1716{
1717	if (track_last_valid_opline) {
1718		use_last_vald_opline = 1;
1719		track_last_valid_opline = 0;
1720	}
1721}
1722
1723static bool zend_jit_trace_uses_initial_ip(void)
1724{
1725	return use_last_vald_opline;
1726}
1727
1728static void zend_jit_set_last_valid_opline(const zend_op *target_opline)
1729{
1730	if (!reuse_ip) {
1731		track_last_valid_opline = 0;
1732		last_valid_opline = target_opline;
1733	}
1734}
1735
1736static void zend_jit_reset_last_valid_opline(void)
1737{
1738	track_last_valid_opline = 0;
1739	last_valid_opline = NULL;
1740}
1741
1742static void zend_jit_start_reuse_ip(void)
1743{
1744	zend_jit_reset_last_valid_opline();
1745	reuse_ip = 1;
1746}
1747
1748static int zend_jit_reuse_ip(dasm_State **Dst)
1749{
1750	if (!reuse_ip) {
1751		zend_jit_start_reuse_ip();
1752		|	// call = EX(call);
1753		|	ldr RX, EX->call
1754	}
1755	return 1;
1756}
1757
1758static void zend_jit_stop_reuse_ip(void)
1759{
1760	reuse_ip = 0;
1761}
1762
1763static int zend_jit_interrupt_handler_stub(dasm_State **Dst)
1764{
1765	|->interrupt_handler:
1766	|	SAVE_IP
1767	|	//EG(vm_interrupt) = 0;
1768	|	MEM_STORE_8_ZTS strb, wzr, executor_globals, vm_interrupt, TMP1
1769	|	//if (EG(timed_out)) {
1770	|	MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, timed_out, TMP1
1771	|	cbz TMP1w, >1
1772	|	//zend_timeout();
1773	|	EXT_CALL zend_timeout, TMP1
1774	|1:
1775	|	//} else if (zend_interrupt_function) {
1776	if (zend_interrupt_function) {
1777		|	//zend_interrupt_function(execute_data);
1778		|	mov CARG1, FP
1779		|	EXT_CALL zend_interrupt_function, TMP1
1780		|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
1781		|	cbz REG0, >1
1782		|	EXT_CALL zend_jit_exception_in_interrupt_handler_helper, TMP1
1783		|1:
1784		|	//ZEND_VM_ENTER();
1785		|	//execute_data = EG(current_execute_data);
1786		|	MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, TMP1
1787		|	LOAD_IP
1788	}
1789	|	//ZEND_VM_CONTINUE()
1790	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1791		|	ADD_HYBRID_SPAD
1792		|	JMP_IP TMP1
1793	} else if (GCC_GLOBAL_REGS) {
1794		|	ldp x29, x30, [sp], # SPAD // stack alignment
1795		|	JMP_IP TMP1
1796	} else {
1797		|	ldp FP, RX, T2                // retore FP and IP
1798		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
1799		|	mov RETVALx, #1               // ZEND_VM_ENTER
1800		|	ret
1801	}
1802
1803	return 1;
1804}
1805
1806static int zend_jit_exception_handler_stub(dasm_State **Dst)
1807{
1808	|->exception_handler:
1809	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1810		const void *handler = zend_get_opcode_handler_func(EG(exception_op));
1811
1812		|	ADD_HYBRID_SPAD
1813		|	EXT_CALL handler, REG0
1814		|	JMP_IP TMP1
1815	} else {
1816		const void *handler = EG(exception_op)->handler;
1817
1818		if (GCC_GLOBAL_REGS) {
1819			|	ldp x29, x30, [sp], # SPAD    // stack alignment
1820			|	EXT_JMP handler, REG0
1821		} else {
1822			|	mov FCARG1x, FP
1823			|	EXT_CALL handler, REG0
1824			|	ldp FP, RX, T2                // retore FP and IP
1825			|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
1826			|	tbnz RETVALw, #31, >1
1827			|	mov RETVALw, #1               // ZEND_VM_ENTER
1828			|1:
1829			|	ret
1830		}
1831	}
1832
1833	return 1;
1834}
1835
1836static int zend_jit_exception_handler_undef_stub(dasm_State **Dst)
1837{
1838	|->exception_handler_undef:
1839	|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, opline_before_exception, REG0
1840	|	ldrb TMP1w, OP:REG0->result_type
1841	|	TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w
1842	|	beq >1
1843	|	ldr REG0w, OP:REG0->result.var
1844	|	add REG0, REG0, FP
1845	|	SET_Z_TYPE_INFO REG0, IS_UNDEF, TMP1w
1846	|1:
1847	|	b ->exception_handler
1848
1849	return 1;
1850}
1851
1852static int zend_jit_exception_handler_free_op1_op2_stub(dasm_State **Dst)
1853{
1854	zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
1855
1856	|->exception_handler_free_op1_op2:
1857	|	UNDEF_OPLINE_RESULT_IF_USED TMP1w, TMP2w
1858	|	ldrb TMP1w, OP:RX->op1_type
1859	|	TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w
1860	|	beq >9
1861	|	ldr REG0w, OP:RX->op1.var
1862	|	add REG0, REG0, FP
1863	|	ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2
1864	|9:
1865	|	ldrb TMP1w, OP:RX->op2_type
1866	|	TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w
1867	|	beq >9
1868	|	ldr REG0w, OP:RX->op2.var
1869	|	add REG0, REG0, FP
1870	|	ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2
1871	|9:
1872	|	b ->exception_handler
1873	return 1;
1874}
1875
1876static int zend_jit_exception_handler_free_op2_stub(dasm_State **Dst)
1877{
1878	zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
1879
1880	|->exception_handler_free_op2:
1881	|	MEM_LOAD_64_ZTS ldr, RX, executor_globals, opline_before_exception, REG0
1882	|	UNDEF_OPLINE_RESULT_IF_USED TMP1w, TMP2w
1883	|	ldrb TMP1w, OP:RX->op2_type
1884	|	TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w
1885	|	beq >9
1886	|	ldr REG0w, OP:RX->op2.var
1887	|	add REG0, REG0, FP
1888	|	ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2
1889	|9:
1890	|	b ->exception_handler
1891	return 1;
1892}
1893
1894static int zend_jit_leave_function_stub(dasm_State **Dst)
1895{
1896	|->leave_function_handler:
1897	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
1898		|	TST_32_WITH_CONST FCARG1w, ZEND_CALL_TOP, TMP1w
1899		|	bne >1
1900		|	EXT_CALL zend_jit_leave_nested_func_helper, REG0
1901		|	ADD_HYBRID_SPAD
1902		|	JMP_IP TMP1
1903		|1:
1904		|	EXT_CALL zend_jit_leave_top_func_helper, REG0
1905		|	ADD_HYBRID_SPAD
1906		|	JMP_IP TMP1
1907	} else {
1908		if (GCC_GLOBAL_REGS) {
1909			|	ldp x29, x30, [sp], # SPAD    // stack alignment
1910		} else {
1911			|	mov FCARG2x, FP
1912			|	ldp FP, RX, T2                // retore FP and IP
1913			|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
1914		}
1915		|	TST_32_WITH_CONST FCARG1w, ZEND_CALL_TOP, TMP1w
1916		|	bne >1
1917		|	EXT_JMP zend_jit_leave_nested_func_helper, REG0
1918		|1:
1919		|	EXT_JMP zend_jit_leave_top_func_helper, REG0
1920	}
1921
1922	return 1;
1923}
1924
1925static int zend_jit_leave_throw_stub(dasm_State **Dst)
1926{
1927	|->leave_throw_handler:
1928	|	// if (opline->opcode != ZEND_HANDLE_EXCEPTION) {
1929	if (GCC_GLOBAL_REGS) {
1930		|	ldrb TMP1w, OP:IP->opcode
1931		|	cmp TMP1w, #ZEND_HANDLE_EXCEPTION
1932		|	beq >5
1933		|	// EG(opline_before_exception) = opline;
1934		|	MEM_STORE_64_ZTS str, IP, executor_globals, opline_before_exception, TMP2
1935		|5:
1936		|	// opline = EG(exception_op);
1937		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2
1938		|	// HANDLE_EXCEPTION()
1939		|	b ->exception_handler
1940	} else {
1941		|	GET_IP TMP1
1942		|	ldrb TMP2w, OP:TMP1->opcode
1943		|	cmp TMP2w, #ZEND_HANDLE_EXCEPTION
1944		|	beq >5
1945		|	// EG(opline_before_exception) = opline;
1946		|	MEM_STORE_64_ZTS str, TMP1, executor_globals, opline_before_exception, TMP2
1947		|5:
1948		|	// opline = EG(exception_op);
1949		|	LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2
1950		|	ldp FP, RX, T2                // retore FP and IP
1951		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
1952		|	mov RETVALx, #2               // ZEND_VM_LEAVE
1953		|	ret
1954	}
1955
1956	return 1;
1957}
1958
1959static int zend_jit_icall_throw_stub(dasm_State **Dst)
1960{
1961	|->icall_throw_handler:
1962	|	// zend_rethrow_exception(zend_execute_data *execute_data)
1963	|	ldr IP, EX->opline
1964	|	// if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) {
1965	|	ldrb TMP1w, OP:IP->opcode
1966	|	cmp TMP1w, #ZEND_HANDLE_EXCEPTION
1967	|	beq >1
1968	|	// EG(opline_before_exception) = opline;
1969	|	MEM_STORE_64_ZTS str, IP, executor_globals, opline_before_exception, TMP2
1970	|1:
1971	|	// opline = EG(exception_op);
1972	|	LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2
1973	||	if (GCC_GLOBAL_REGS) {
1974	|		str IP, EX->opline
1975	||	}
1976	|	// HANDLE_EXCEPTION()
1977	|	b ->exception_handler
1978
1979	return 1;
1980}
1981
1982static int zend_jit_throw_cannot_pass_by_ref_stub(dasm_State **Dst)
1983{
1984	zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
1985
1986	|->throw_cannot_pass_by_ref:
1987	|	ldr REG0, EX->opline
1988	|	ldr REG1w, OP:REG0->result.var
1989	|	add REG1, REG1, RX
1990	|	SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w
1991	|	// last EX(call) frame may be delayed
1992	|	ldr TMP1, EX->call
1993	|	cmp RX, TMP1
1994	|	beq >1
1995	|	ldr REG1, EX->call
1996	|	str REG1, EX:RX->prev_execute_data
1997	|	str RX, EX->call
1998	|1:
1999	|	mov RX, REG0
2000	|	ldr FCARG1w, OP:REG0->op2.num
2001	|	EXT_CALL zend_cannot_pass_by_reference, REG0
2002	|	ldrb TMP1w, OP:RX->op1_type
2003	|	cmp TMP1w, #IS_TMP_VAR
2004	|	bne >9
2005	|	ldr REG0w, OP:RX->op1.var
2006	|	add REG0, REG0, FP
2007	|	ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2
2008	|9:
2009	|	b ->exception_handler
2010
2011	return 1;
2012}
2013
2014static int zend_jit_undefined_offset_ex_stub(dasm_State **Dst)
2015{
2016	|->undefined_offset_ex:
2017	|	SAVE_IP
2018	|	b ->undefined_offset
2019
2020	return 1;
2021}
2022
2023static int zend_jit_undefined_offset_stub(dasm_State **Dst)
2024{
2025	|->undefined_offset:
2026#ifdef __APPLE__
2027	|	stp x29, x30, [sp, # -16]!
2028	|	mov x29, sp
2029#endif
2030	|	//sub r4, 8
2031	|	ldr REG0, EX->opline
2032	|	ldr REG1w, OP:REG0->result.var
2033	|	add REG1, REG1, FP
2034	|	SET_Z_TYPE_INFO REG1, IS_NULL, TMP1w
2035	|	ldrb REG1w, OP:REG0->op2_type
2036	|	cmp REG1w, #IS_CONST
2037	|	bne >2
2038	|	ldrsw REG1, OP:REG0->op2.constant
2039	|	add REG0, REG0, REG1
2040	|	b >3
2041	|2:
2042	|	ldr REG0w, OP:REG0->op2.var
2043	|	add REG0, REG0, FP
2044	|3:
2045	|	mov CARG1, #E_WARNING
2046	|	LOAD_ADDR CARG2, "Undefined array key " ZEND_LONG_FMT
2047	|	ldr CARG3, [REG0]
2048#ifdef __APPLE__
2049	|	str CARG3, [sp, #-16]!
2050	|	EXT_CALL zend_error, REG0
2051	|	add sp, sp, #16
2052	|	ldp x29, x30, [sp], #16
2053	|	ret
2054#else
2055	|	EXT_JMP zend_error, REG0 // tail call
2056	|	//add r4, 8 // stack alignment
2057	|	//ret
2058#endif
2059
2060	return 1;
2061}
2062
2063static int zend_jit_undefined_index_ex_stub(dasm_State **Dst)
2064{
2065	|->undefined_index_ex:
2066	|	SAVE_IP
2067	|	b ->undefined_index
2068
2069	return 1;
2070}
2071
2072static int zend_jit_undefined_index_stub(dasm_State **Dst)
2073{
2074	|->undefined_index:
2075#ifdef __APPLE__
2076	|	stp x29, x30, [sp, # -16]!
2077	|	mov x29, sp
2078#endif
2079	|	//sub r4, 8
2080	|	ldr REG0, EX->opline
2081	|	ldr REG1w, OP:REG0->result.var
2082	|	add REG1, REG1, FP
2083	|	SET_Z_TYPE_INFO REG1, IS_NULL, TMP1w
2084	|	ldrb REG1w, OP:REG0->op2_type
2085	|	cmp REG1w, #IS_CONST
2086	|	bne >2
2087	|	ldrsw REG1, OP:REG0->op2.constant
2088	|	add REG0, REG0, REG1
2089	|	b >3
2090	|2:
2091	|	ldr REG0w, OP:REG0->op2.var
2092	|	add REG0, REG0, FP
2093	|3:
2094	|	mov CARG1, #E_WARNING
2095	|	LOAD_ADDR CARG2, "Undefined array key \"%s\""
2096	|	ldr CARG3, [REG0]
2097	|	add CARG3, CARG3, #offsetof(zend_string, val)
2098#ifdef __APPLE__
2099	|	str CARG3, [sp, #-16]!
2100	|	EXT_CALL zend_error, REG0
2101	|	add sp, sp, #16
2102	|	ldp x29, x30, [sp], #16
2103	|	ret
2104#else
2105	|	EXT_JMP zend_error, REG0 // tail call
2106	|	//add r4, 8
2107	|	//ret
2108#endif
2109
2110	return 1;
2111}
2112
2113static int zend_jit_cannot_add_element_ex_stub(dasm_State **Dst)
2114{
2115	|->cannot_add_element_ex:
2116	|	SAVE_IP
2117	|	b ->cannot_add_element
2118
2119	return 1;
2120}
2121
2122static int zend_jit_cannot_add_element_stub(dasm_State **Dst)
2123{
2124	|->cannot_add_element:
2125	|	// sub r4, 8
2126	|	ldr REG0, EX->opline
2127	|	ldrb TMP1w, OP:REG0->result_type
2128	|	cmp TMP1w, #IS_UNUSED
2129	|	beq >1
2130	|	ldr REG0w, OP:REG0->result.var
2131	|	add REG0, REG0, FP
2132	|	SET_Z_TYPE_INFO REG0, IS_NULL, TMP1w
2133	|1:
2134	|	mov CARG1, xzr
2135	|	LOAD_ADDR CARG2, "Cannot add element to the array as the next element is already occupied"
2136	|	EXT_JMP zend_throw_error, REG0 // tail call
2137	|	// add r4, 8
2138	|	//ret
2139
2140	return 1;
2141}
2142
2143static int zend_jit_undefined_function_stub(dasm_State **Dst)
2144{
2145	|->undefined_function:
2146	|	ldr REG0, EX->opline
2147	|	mov CARG1, xzr
2148	|	LOAD_ADDR CARG2, "Call to undefined function %s()"
2149	|	ldrsw CARG3, [REG0, #offsetof(zend_op, op2.constant)]
2150	|	ldr CARG3, [REG0, CARG3]
2151	|	add CARG3, CARG3, #offsetof(zend_string, val)
2152#ifdef __APPLE__
2153	|	str CARG3, [sp, #-16]!
2154#endif
2155	|	EXT_CALL zend_throw_error, REG0
2156#ifdef __APPLE__
2157	|	add sp, sp, #16
2158#endif
2159	|	b ->exception_handler
2160	return 1;
2161}
2162
2163static int zend_jit_negative_shift_stub(dasm_State **Dst)
2164{
2165	|->negative_shift:
2166	|	ldr RX, EX->opline
2167	|	LOAD_ADDR CARG1, zend_ce_arithmetic_error
2168	|	LOAD_ADDR CARG2, "Bit shift by negative number"
2169	|	EXT_CALL zend_throw_error, REG0
2170	|	b ->exception_handler_free_op1_op2
2171	return 1;
2172}
2173
2174static int zend_jit_mod_by_zero_stub(dasm_State **Dst)
2175{
2176	|->mod_by_zero:
2177	|	ldr RX, EX->opline
2178	|	LOAD_ADDR CARG1, zend_ce_division_by_zero_error
2179	|	LOAD_ADDR CARG2, "Modulo by zero"
2180	|	EXT_CALL zend_throw_error, REG0
2181	|	b ->exception_handler_free_op1_op2
2182	return 1;
2183}
2184
2185static int zend_jit_invalid_this_stub(dasm_State **Dst)
2186{
2187	|->invalid_this:
2188	|	UNDEF_OPLINE_RESULT TMP1w
2189	|	mov CARG1, xzr
2190	|	LOAD_ADDR CARG2, "Using $this when not in object context"
2191	|	EXT_CALL zend_throw_error, REG0
2192	|	b ->exception_handler
2193
2194	return 1;
2195}
2196
2197static int zend_jit_hybrid_runtime_jit_stub(dasm_State **Dst)
2198{
2199	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2200		return 1;
2201	}
2202
2203	|->hybrid_runtime_jit:
2204	|	EXT_CALL zend_runtime_jit, REG0
2205	|	JMP_IP TMP1
2206	return 1;
2207}
2208
2209static int zend_jit_hybrid_profile_jit_stub(dasm_State **Dst)
2210{
2211	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2212		return 1;
2213	}
2214
2215	|->hybrid_profile_jit:
2216	|	// ++zend_jit_profile_counter;
2217	|	LOAD_ADDR REG0, &zend_jit_profile_counter
2218	|	ldr TMP1, [REG0]
2219	|	add TMP1, TMP1, #1
2220	|	str TMP1, [REG0]
2221	|	// op_array = (zend_op_array*)EX(func);
2222	|	ldr REG0, EX->func
2223	|	// run_time_cache = EX(run_time_cache);
2224	|	ldr REG2, EX->run_time_cache
2225	|	// jit_extension = (const void*)ZEND_FUNC_INFO(op_array);
2226	|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
2227	|	// ++ZEND_COUNTER_INFO(op_array)
2228	||	if ((zend_jit_profile_counter_rid * sizeof(void*)) > LDR_STR_PIMM64) {
2229	|		LOAD_32BIT_VAL TMP1, (zend_jit_profile_counter_rid * sizeof(void*))
2230	|		ldr TMP2, [REG2, TMP1]
2231	|		add TMP2, TMP2, #1
2232	|		str TMP2, [REG2, TMP1]
2233	||	} else {
2234	|		ldr TMP2, [REG2, #(zend_jit_profile_counter_rid * sizeof(void*))]
2235	|		add TMP2, TMP2, #1
2236	|		str TMP2, [REG2, #(zend_jit_profile_counter_rid * sizeof(void*))]
2237	||	}
2238	|	// return ((zend_vm_opcode_handler_t)jit_extension->orig_handler)()
2239	|	ldr TMP1, [REG0, #offsetof(zend_jit_op_array_extension, orig_handler)]
2240	|	br TMP1
2241	return 1;
2242}
2243
2244static int zend_jit_hybrid_hot_code_stub(dasm_State **Dst)
2245{
2246	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2247		return 1;
2248	}
2249
2250	|->hybrid_hot_code:
2251	||	ZEND_ASSERT(ZEND_JIT_COUNTER_INIT <= MOVZ_IMM);
2252	|	movz TMP1w, #ZEND_JIT_COUNTER_INIT
2253	|	strh TMP1w, [REG2]
2254	|	mov FCARG1x, FP
2255	|	GET_IP FCARG2x
2256	|	EXT_CALL zend_jit_hot_func, REG0
2257	|	JMP_IP TMP1
2258	return 1;
2259}
2260
2261/*
2262 * This code is based Mike Pall's "Hashed profile counters" idea, implemented
2263 * in LuaJIT. The full description may be found in "LuaJIT 2.0 intellectual
2264 * property disclosure and research opportunities" email
2265 * at http://lua-users.org/lists/lua-l/2009-11/msg00089.html
2266 *
2267 * In addition we use a variation of Knuth's multiplicative hash function
2268 * described at https://code.i-harness.com/en/q/a21ce
2269 *
2270 * uint64_t hash(uint64_t x) {
2271 *    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
2272 *    x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
2273 *    x = x ^ (x >> 31);
2274 *    return x;
2275 * }
2276 *
2277 * uint_32_t hash(uint32_t x) {
2278 *    x = ((x >> 16) ^ x) * 0x45d9f3b;
2279 *    x = ((x >> 16) ^ x) * 0x45d9f3b;
2280 *    x = (x >> 16) ^ x;
2281 *    return x;
2282 * }
2283 *
2284 */
2285static int zend_jit_hybrid_hot_counter_stub(dasm_State **Dst, uint32_t cost)
2286{
2287	|	ldr REG0, EX->func
2288	|	ldr REG1, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
2289	|	ldr REG2, [REG1, #offsetof(zend_jit_op_array_hot_extension, counter)]
2290	|	ldrh TMP2w, [REG2]
2291	|	ADD_SUB_32_WITH_CONST subs, TMP2w, TMP2w, cost, TMP1w
2292	|	strh TMP2w, [REG2]
2293	|	ble ->hybrid_hot_code
2294	|	GET_IP REG2
2295	|	ldr TMP1, [REG0, #offsetof(zend_op_array, opcodes)]
2296	|	sub REG2, REG2, TMP1
2297	|	// divide by sizeof(zend_op)
2298	||	ZEND_ASSERT(sizeof(zend_op) == 32);
2299	|	add TMP1, REG1, REG2, asr #2
2300	|	ldr TMP1, [TMP1, #offsetof(zend_jit_op_array_hot_extension, orig_handlers)]
2301	|	br TMP1
2302	return 1;
2303}
2304
2305static int zend_jit_hybrid_func_hot_counter_stub(dasm_State **Dst)
2306{
2307	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
2308		return 1;
2309	}
2310
2311	|->hybrid_func_hot_counter:
2312
2313	return zend_jit_hybrid_hot_counter_stub(Dst,
2314		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func)));
2315}
2316
2317static int zend_jit_hybrid_loop_hot_counter_stub(dasm_State **Dst)
2318{
2319	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
2320		return 1;
2321	}
2322
2323	|->hybrid_loop_hot_counter:
2324
2325	return zend_jit_hybrid_hot_counter_stub(Dst,
2326		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
2327}
2328
2329static int zend_jit_hybrid_hot_trace_stub(dasm_State **Dst)
2330{
2331	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
2332		return 1;
2333	}
2334
2335	// On entry from counter stub:
2336	//   REG2 -> zend_op_trace_info.counter
2337
2338	|->hybrid_hot_trace:
2339	|	mov TMP1w, #ZEND_JIT_COUNTER_INIT
2340	|	strh TMP1w, [REG2]
2341	|	mov FCARG1x, FP
2342	|	GET_IP FCARG2x
2343	|	EXT_CALL zend_jit_trace_hot_root, REG0
2344	|	tbnz RETVALw, #31, >1  // Result is < 0 on failure.
2345	|	MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, REG0
2346	|	LOAD_IP
2347	|	JMP_IP TMP1
2348	|1:
2349	|	EXT_JMP zend_jit_halt_op->handler, REG0
2350
2351	return 1;
2352}
2353
2354static int zend_jit_hybrid_trace_counter_stub(dasm_State **Dst, uint32_t cost)
2355{
2356	|	ldr REG0, EX->func
2357	|	ldr REG1, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
2358	|	ldr REG1, [REG1, #offsetof(zend_jit_op_array_trace_extension, offset)]
2359	|	add TMP1, REG1, IP
2360	|	ldr REG2, [TMP1, #offsetof(zend_op_trace_info, counter)]
2361	|	ldrh TMP2w, [REG2]
2362	|	ADD_SUB_32_WITH_CONST subs, TMP2w, TMP2w, cost, TMP1w
2363	|	strh TMP2w, [REG2]
2364	|	ble ->hybrid_hot_trace
2365	// Note: "REG1 + IP" is re-calculated as TMP1 is used as temporary register by the prior
2366	// ADD_SUB_32_WITH_CONST. Will optimize in the future if more temporary registers are available.
2367	|	add TMP1, REG1, IP
2368	|	ldr TMP2, [TMP1, #offsetof(zend_op_trace_info, orig_handler)]
2369	|	br TMP2
2370
2371	return 1;
2372}
2373
2374static int zend_jit_hybrid_func_trace_counter_stub(dasm_State **Dst)
2375{
2376	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) {
2377		return 1;
2378	}
2379
2380	|->hybrid_func_trace_counter:
2381
2382	return zend_jit_hybrid_trace_counter_stub(Dst,
2383		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1)  / JIT_G(hot_func)));
2384}
2385
2386static int zend_jit_hybrid_ret_trace_counter_stub(dasm_State **Dst)
2387{
2388	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_return)) {
2389		return 1;
2390	}
2391
2392	|->hybrid_ret_trace_counter:
2393
2394	return zend_jit_hybrid_trace_counter_stub(Dst,
2395		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_return) - 1) / JIT_G(hot_return)));
2396}
2397
2398static int zend_jit_hybrid_loop_trace_counter_stub(dasm_State **Dst)
2399{
2400	if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) {
2401		return 1;
2402	}
2403
2404	|->hybrid_loop_trace_counter:
2405
2406	return zend_jit_hybrid_trace_counter_stub(Dst,
2407		((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop)));
2408}
2409
2410static int zend_jit_trace_halt_stub(dasm_State **Dst)
2411{
2412	|->trace_halt:
2413	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2414		|	ADD_HYBRID_SPAD
2415		|	EXT_JMP zend_jit_halt_op->handler, REG0
2416	} else if (GCC_GLOBAL_REGS) {
2417		|	ldp x29, x30, [sp], # SPAD    // stack alignment
2418		|	mov IP, xzr                   // PC must be zero
2419		|	ret
2420	} else {
2421		|	ldp FP, RX, T2                // retore FP and IP
2422		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
2423		|	movn RETVALx, #0              // ZEND_VM_RETURN (-1)
2424		|	ret
2425	}
2426	return 1;
2427}
2428
2429static int zend_jit_trace_exit_stub(dasm_State **Dst)
2430{
2431	|->trace_exit:
2432	|
2433	|	// Save CPU registers(32 GP regs + 32 FP regs) on stack in the order of d31 to x0
2434	|
2435	|	stp d30, d31, [sp, #-16]!
2436	|	stp d28, d29, [sp, #-16]!
2437	|	stp d26, d27, [sp, #-16]!
2438	|	stp d24, d25, [sp, #-16]!
2439	|	stp d22, d23, [sp, #-16]!
2440	|	stp d20, d21, [sp, #-16]!
2441	|	stp d18, d19, [sp, #-16]!
2442	|	stp d16, d17, [sp, #-16]!
2443	|	//stp d14, d15, [sp, #-16]!     // we don't use preserved registers yet
2444	|	//stp d12, d13, [sp, #-16]!
2445	|	//stp d10, d11, [sp, #-16]!
2446	|	//stp d8, d9, [sp, #-16]!
2447	|	stp d6, d7, [sp, #(-16*5)]!
2448	|	stp d4, d5, [sp, #-16]!
2449	|	stp d2, d3, [sp, #-16]!
2450	|	stp d0, d1, [sp, #-16]!
2451	|
2452	|	//str x30, [sp, #-16]!          // we don't use callee-saved registers yet (x31 can be omitted)
2453	|	stp x28, x29, [sp, #(-16*2)]!   // we have to store RX (x28)
2454	|	//stp x26, x27, [sp, #-16]!     // we don't use callee-saved registers yet
2455	|	//stp x24, x25, [sp, #-16]!
2456	|	//stp x22, x23, [sp, #-16]!
2457	|	//stp x20, x21, [sp, #-16]!
2458	|	//stp x18, x19, [sp, #-16]!
2459	|	//stp x16, x17, [sp, #-16]!     // we don't need temporary registers
2460	|	stp x14, x15, [sp, #-(16*7)]!
2461	|	stp x12, x13, [sp, #-16]!
2462	|	stp x10, x11, [sp, #-16]!
2463	|	stp x8, x9, [sp, #-16]!
2464	|	stp x6, x7, [sp, #-16]!
2465	|	stp x4, x5, [sp, #-16]!
2466	|	stp x2, x3, [sp, #-16]!
2467	|	stp x0, x1, [sp, #-16]!
2468	|
2469	|	mov FCARG1w, TMP1w              // exit_num
2470	|	mov FCARG2x, sp
2471	|
2472	|	// EX(opline) = opline
2473	|	SAVE_IP
2474	|	// zend_jit_trace_exit(trace_num, exit_num)
2475	|	EXT_CALL zend_jit_trace_exit, REG0
2476	|
2477	|	add sp, sp, #(32 * 16)          // restore sp
2478	|
2479
2480	|	tst RETVALw, RETVALw
2481	|	bne >1  // not zero
2482
2483	|	// execute_data = EG(current_execute_data)
2484	|	MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, REG0
2485	|	// opline = EX(opline)
2486	|	LOAD_IP
2487
2488	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2489		|	ADD_HYBRID_SPAD
2490		|	JMP_IP TMP1
2491	} else if (GCC_GLOBAL_REGS) {
2492		|	ldp x29, x30, [sp], # SPAD    // stack alignment
2493		|	JMP_IP TMP1
2494	} else {
2495		|	ldp FP, RX, T2                // retore FP and IP
2496		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
2497		|	mov RETVALx, #1               // ZEND_VM_ENTER
2498		|	ret
2499	}
2500
2501	|1:
2502	|	blt ->trace_halt
2503
2504	|	// execute_data = EG(current_execute_data)
2505	|	MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, REG0
2506	|	// opline = EX(opline)
2507	|	LOAD_IP
2508
2509	|	// check for interrupt (try to avoid this ???)
2510	|	MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1
2511	|	cbnz TMP1w, ->interrupt_handler
2512
2513	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2514		|	ADD_HYBRID_SPAD
2515		|	ldr REG0, EX->func
2516		|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
2517		|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
2518		|	ldr REG0, [IP, REG0]
2519		|	br REG0
2520	} else if (GCC_GLOBAL_REGS) {
2521		|	ldp x29, x30, [sp], # SPAD    // stack alignment
2522		|	ldr REG0, EX->func
2523		|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
2524		|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
2525		|	ldr REG0, [IP, REG0]
2526		|	br REG0
2527	} else {
2528		|	ldr IP, EX->opline
2529		|	mov FCARG1x, FP
2530		|	ldr REG0, EX->func
2531		|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
2532		|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
2533		|	ldr REG0, [IP, REG0]
2534		|	blr REG0
2535		|
2536		|	tst RETVALw, RETVALw
2537		|	blt ->trace_halt
2538		|
2539		|	ldp FP, RX, T2                // retore FP and IP
2540		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
2541		|	mov RETVALx, #1               // ZEND_VM_ENTER
2542		|	ret
2543	}
2544
2545	return 1;
2546}
2547
2548static int zend_jit_trace_escape_stub(dasm_State **Dst)
2549{
2550	|->trace_escape:
2551	|
2552	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2553		|	ADD_HYBRID_SPAD
2554		|	JMP_IP, TMP1
2555	} else if (GCC_GLOBAL_REGS) {
2556		|	ldp x29, x30, [sp], # SPAD    // stack alignment
2557		|	JMP_IP, TMP1
2558	} else {
2559		|	ldp FP, RX, T2                // retore FP and IP
2560		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
2561		|	mov RETVALx, #1               // ZEND_VM_ENTER
2562		|	ret
2563	}
2564
2565	return 1;
2566}
2567
2568/* Keep 32 exit points in a single code block */
2569#define ZEND_JIT_EXIT_POINTS_SPACING    4 // bl = bytes
2570#define ZEND_JIT_EXIT_POINTS_PER_GROUP 32 // number of continuous exit points
2571
2572static int zend_jit_trace_exit_group_stub(dasm_State **Dst, uint32_t n)
2573{
2574	uint32_t i;
2575
2576	|	bl >2
2577	|1:
2578	for (i = 1; i < ZEND_JIT_EXIT_POINTS_PER_GROUP; i++) {
2579		|	bl >2
2580	}
2581	|2:
2582	|	adr TMP1, <1
2583	|	sub TMP1, lr, TMP1
2584	|	lsr TMP1, TMP1, #2
2585	if (n) {
2586		|	ADD_SUB_32_WITH_CONST add, TMP1w, TMP1w, n, TMP2w
2587	}
2588	|	b ->trace_exit // pass exit_num in TMP1w
2589
2590	return 1;
2591}
2592
2593#ifdef CONTEXT_THREADED_JIT
2594static int zend_jit_context_threaded_call_stub(dasm_State **Dst)
2595{
2596	|->context_threaded_call:
2597	|	NIY_STUB	// TODO
2598	return 1;
2599}
2600#endif
2601
2602static int zend_jit_assign_const_stub(dasm_State **Dst)
2603{
2604	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2605	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2606	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
2607
2608	|->assign_const:
2609	|	stp x29, x30, [sp, #-32]!
2610	|	mov x29, sp
2611	if (!zend_jit_assign_to_variable(
2612			Dst, NULL,
2613			var_addr, var_addr, -1, -1,
2614			IS_CONST, val_addr, val_info,
2615			0, 0)) {
2616		return 0;
2617	}
2618	|	ldp x29, x30, [sp], #32
2619	|	ret
2620	return 1;
2621}
2622
2623static int zend_jit_assign_tmp_stub(dasm_State **Dst)
2624{
2625	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2626	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2627	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN;
2628
2629	|->assign_tmp:
2630	|	stp x29, x30, [sp, #-32]!
2631	|	mov x29, sp
2632	if (!zend_jit_assign_to_variable(
2633			Dst, NULL,
2634			var_addr, var_addr, -1, -1,
2635			IS_TMP_VAR, val_addr, val_info,
2636			0, 0)) {
2637		return 0;
2638	}
2639	|	ldp x29, x30, [sp], #32
2640	|	ret
2641	return 1;
2642}
2643
2644static int zend_jit_assign_var_stub(dasm_State **Dst)
2645{
2646	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2647	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2648	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF;
2649
2650	|->assign_var:
2651	|	stp x29, x30, [sp, #-32]!
2652	|	mov x29, sp
2653	if (!zend_jit_assign_to_variable(
2654			Dst, NULL,
2655			var_addr, var_addr, -1, -1,
2656			IS_VAR, val_addr, val_info,
2657			0, 0)) {
2658		return 0;
2659	}
2660	|	ldp x29, x30, [sp], #32
2661	|	ret
2662	return 1;
2663}
2664
2665static int zend_jit_assign_cv_noref_stub(dasm_State **Dst)
2666{
2667	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2668	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2669	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN/*|MAY_BE_UNDEF*/;
2670
2671	|->assign_cv_noref:
2672	|	stp x29, x30, [sp, #-32]!
2673	|	mov x29, sp
2674	if (!zend_jit_assign_to_variable(
2675			Dst, NULL,
2676			var_addr, var_addr, -1, -1,
2677			IS_CV, val_addr, val_info,
2678			0, 0)) {
2679		return 0;
2680	}
2681	|	ldp x29, x30, [sp], #32
2682	|	ret
2683	return 1;
2684}
2685
2686static int zend_jit_assign_cv_stub(dasm_State **Dst)
2687{
2688	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
2689	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
2690	uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF/*|MAY_BE_UNDEF*/;
2691
2692	|->assign_cv:
2693	|	stp x29, x30, [sp, #-32]!
2694	|	mov x29, sp
2695	if (!zend_jit_assign_to_variable(
2696			Dst, NULL,
2697			var_addr, var_addr, -1, -1,
2698			IS_CV, val_addr, val_info,
2699			0, 0)) {
2700		return 0;
2701	}
2702	|	ldp x29, x30, [sp], #32
2703	|	ret
2704	return 1;
2705}
2706
2707static const zend_jit_stub zend_jit_stubs[] = {
2708	JIT_STUB(interrupt_handler,         SP_ADJ_JIT,  SP_ADJ_VM),
2709	JIT_STUB(exception_handler,         SP_ADJ_JIT,  SP_ADJ_VM),
2710	JIT_STUB(exception_handler_undef,   SP_ADJ_JIT,  SP_ADJ_VM),
2711	JIT_STUB(exception_handler_free_op1_op2, SP_ADJ_JIT,  SP_ADJ_VM),
2712	JIT_STUB(exception_handler_free_op2,     SP_ADJ_JIT,  SP_ADJ_VM),
2713	JIT_STUB(leave_function,            SP_ADJ_JIT,  SP_ADJ_VM),
2714	JIT_STUB(leave_throw,               SP_ADJ_JIT,  SP_ADJ_VM),
2715	JIT_STUB(icall_throw,               SP_ADJ_JIT,  SP_ADJ_VM),
2716	JIT_STUB(throw_cannot_pass_by_ref,  SP_ADJ_JIT,  SP_ADJ_VM),
2717	JIT_STUB(undefined_offset,          SP_ADJ_JIT,  SP_ADJ_VM),
2718	JIT_STUB(undefined_index,           SP_ADJ_JIT,  SP_ADJ_VM),
2719	JIT_STUB(cannot_add_element,        SP_ADJ_JIT,  SP_ADJ_VM),
2720	JIT_STUB(undefined_offset_ex,       SP_ADJ_JIT,  SP_ADJ_VM),
2721	JIT_STUB(undefined_index_ex,        SP_ADJ_JIT,  SP_ADJ_VM),
2722	JIT_STUB(cannot_add_element_ex,     SP_ADJ_JIT,  SP_ADJ_VM),
2723	JIT_STUB(undefined_function,        SP_ADJ_JIT,  SP_ADJ_VM),
2724	JIT_STUB(negative_shift,            SP_ADJ_JIT,  SP_ADJ_VM),
2725	JIT_STUB(mod_by_zero,               SP_ADJ_JIT,  SP_ADJ_VM),
2726	JIT_STUB(invalid_this,              SP_ADJ_JIT,  SP_ADJ_VM),
2727	JIT_STUB(trace_halt,                SP_ADJ_JIT,  SP_ADJ_VM),
2728	JIT_STUB(trace_exit,                SP_ADJ_JIT,  SP_ADJ_VM),
2729	JIT_STUB(trace_escape,              SP_ADJ_JIT,  SP_ADJ_VM),
2730	JIT_STUB(hybrid_runtime_jit,        SP_ADJ_VM,   SP_ADJ_NONE),
2731	JIT_STUB(hybrid_profile_jit,        SP_ADJ_VM,   SP_ADJ_NONE),
2732	JIT_STUB(hybrid_hot_code,           SP_ADJ_VM,   SP_ADJ_NONE),
2733	JIT_STUB(hybrid_func_hot_counter,   SP_ADJ_VM,   SP_ADJ_NONE),
2734	JIT_STUB(hybrid_loop_hot_counter,   SP_ADJ_VM,   SP_ADJ_NONE),
2735	JIT_STUB(hybrid_hot_trace,          SP_ADJ_VM,   SP_ADJ_NONE),
2736	JIT_STUB(hybrid_func_trace_counter, SP_ADJ_VM,   SP_ADJ_NONE),
2737	JIT_STUB(hybrid_ret_trace_counter,  SP_ADJ_VM,   SP_ADJ_NONE),
2738	JIT_STUB(hybrid_loop_trace_counter, SP_ADJ_VM,   SP_ADJ_NONE),
2739	JIT_STUB(assign_const,              SP_ADJ_RET,  SP_ADJ_ASSIGN),
2740	JIT_STUB(assign_tmp,                SP_ADJ_RET,  SP_ADJ_ASSIGN),
2741	JIT_STUB(assign_var,                SP_ADJ_RET,  SP_ADJ_ASSIGN),
2742	JIT_STUB(assign_cv_noref,           SP_ADJ_RET,  SP_ADJ_ASSIGN),
2743	JIT_STUB(assign_cv,                 SP_ADJ_RET,  SP_ADJ_ASSIGN),
2744#ifdef CONTEXT_THREADED_JIT
2745	JIT_STUB(context_threaded_call,     SP_ADJ_NONE, SP_ADJ_NONE),
2746#endif
2747};
2748
2749#ifdef HAVE_GDB
2750# if 0
2751typedef struct _Unwind_Context _Unwind_Context;
2752typedef int (*_Unwind_Trace_Fn)(_Unwind_Context *, void *);
2753extern int _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
2754extern uintptr_t _Unwind_GetCFA(_Unwind_Context *);
2755
2756typedef struct _zend_jit_unwind_arg {
2757	int cnt;
2758	uintptr_t cfa[3];
2759} zend_jit_unwind_arg;
2760
2761static int zend_jit_unwind_cb(_Unwind_Context *ctx, void *a)
2762{
2763	zend_jit_unwind_arg *arg = (zend_jit_unwind_arg*)a;
2764	arg->cfa[arg->cnt] = _Unwind_GetCFA(ctx);
2765	arg->cnt++;
2766	if (arg->cnt == 3) {
2767		return 5; // _URC_END_OF_STACK
2768	}
2769	return 0;     // _URC_NO_REASON;
2770}
2771
2772static void ZEND_FASTCALL zend_jit_touch_vm_stack_data(void *vm_stack_data)
2773{
2774	zend_jit_unwind_arg arg;
2775
2776	memset(&arg, 0, sizeof(arg));
2777	_Unwind_Backtrace(zend_jit_unwind_cb, &arg);
2778	if (arg.cnt == 3) {
2779		sp_adj[SP_ADJ_VM] = arg.cfa[2] - arg.cfa[1];
2780	}
2781}
2782# else
2783static void ZEND_FASTCALL zend_jit_touch_vm_stack_data(void *vm_stack_data)
2784{
2785	uintptr_t ret;
2786
2787	__asm__ (
2788		"ldr %0, [x29]\n\t"
2789		"sub %0 ,%0, x29"
2790		: "=r"(ret));
2791
2792	sp_adj[SP_ADJ_VM] = ret;
2793}
2794# endif
2795
2796extern void (ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data);
2797
2798static zend_never_inline void zend_jit_set_sp_adj_vm(void)
2799{
2800	void (ZEND_FASTCALL *orig_zend_touch_vm_stack_data)(void *);
2801
2802	orig_zend_touch_vm_stack_data = zend_touch_vm_stack_data;
2803	zend_touch_vm_stack_data = zend_jit_touch_vm_stack_data;
2804	execute_ex(NULL);                                        // set sp_adj[SP_ADJ_VM]
2805	zend_touch_vm_stack_data = orig_zend_touch_vm_stack_data;
2806}
2807#endif
2808
2809static int zend_jit_setup(void)
2810{
2811	allowed_opt_flags = 0;
2812
2813#if ZTS
2814	tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
2815	ZEND_ASSERT(tsrm_ls_cache_tcb_offset != 0);
2816#endif
2817
2818    memset(sp_adj, 0, sizeof(sp_adj));
2819#ifdef HAVE_GDB
2820	sp_adj[SP_ADJ_RET] = 0;
2821	sp_adj[SP_ADJ_ASSIGN] = 32;
2822	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2823		zend_jit_set_sp_adj_vm();                                // set sp_adj[SP_ADJ_VM]
2824#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
2825		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM] + HYBRID_SPAD; // sub r4, HYBRID_SPAD
2826#else
2827		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM];
2828#endif
2829	} else if (GCC_GLOBAL_REGS) {
2830		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + SPAD;       // sub r4, SPAD
2831	} else {
2832		|| sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + NR_SPAD;    // sub r4, NR_SPAD
2833	}
2834#endif
2835
2836	return SUCCESS;
2837}
2838
2839static ZEND_ATTRIBUTE_UNUSED int zend_jit_trap(dasm_State **Dst)
2840{
2841	|	brk #0
2842	return 1;
2843}
2844
2845static int zend_jit_align_func(dasm_State **Dst)
2846{
2847	reuse_ip = 0;
2848	delayed_call_chain = 0;
2849	last_valid_opline = NULL;
2850	use_last_vald_opline = 0;
2851	track_last_valid_opline = 0;
2852	jit_return_label = -1;
2853	|.align 16
2854	return 1;
2855}
2856
2857static int zend_jit_prologue(dasm_State **Dst)
2858{
2859	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
2860		|	SUB_HYBRID_SPAD
2861	} else if (GCC_GLOBAL_REGS) {
2862		|	stp x29, x30, [sp, # -SPAD]!    // stack alignment
2863		|//	mov x29, sp
2864	} else {
2865		|	stp x29, x30, [sp, # -NR_SPAD]! // stack alignment
2866		|//	mov	x29, sp
2867		|	stp FP, RX, T2                  // save FP and IP
2868		|	mov FP, FCARG1x
2869	}
2870	return 1;
2871}
2872
2873static int zend_jit_label(dasm_State **Dst, unsigned int label)
2874{
2875	|=>label:
2876	return 1;
2877}
2878
2879static int zend_jit_save_call_chain(dasm_State **Dst, uint32_t call_level)
2880{
2881	|	// call->prev_execute_data = EX(call);
2882	if (call_level == 1) {
2883		|	str xzr, EX:RX->prev_execute_data
2884	} else {
2885		|	ldr REG0, EX->call
2886		|	str REG0, EX:RX->prev_execute_data
2887	}
2888	|	// EX(call) = call;
2889	|	str RX, EX->call
2890
2891	delayed_call_chain = 0;
2892
2893	return 1;
2894}
2895
2896static int zend_jit_set_ip(dasm_State **Dst, const zend_op *opline)
2897{
2898	if (last_valid_opline == opline) {
2899		zend_jit_use_last_valid_opline();
2900	} else if (GCC_GLOBAL_REGS && last_valid_opline) {
2901		zend_jit_use_last_valid_opline();
2902		|	LOAD_64BIT_VAL TMP1, (opline - last_valid_opline) * sizeof(zend_op)
2903		|	ADD_IP TMP1, TMP2
2904	} else {
2905		|	LOAD_IP_ADDR opline
2906	}
2907	zend_jit_set_last_valid_opline(opline);
2908
2909	return 1;
2910}
2911
2912static int zend_jit_set_ip_ex(dasm_State **Dst, const zend_op *opline, bool set_ip_reg)
2913{
2914	return zend_jit_set_ip(Dst, opline);
2915}
2916
2917static int zend_jit_set_valid_ip(dasm_State **Dst, const zend_op *opline)
2918{
2919	if (delayed_call_chain) {
2920		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
2921			return 0;
2922		}
2923	}
2924	if (!zend_jit_set_ip(Dst, opline)) {
2925		return 0;
2926	}
2927	reuse_ip = 0;
2928	return 1;
2929}
2930
2931static int zend_jit_check_timeout(dasm_State **Dst, const zend_op *opline, const void *exit_addr)
2932{
2933	|	MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1
2934	if (exit_addr) {
2935		|	cbnz TMP1w, &exit_addr
2936	} else if (last_valid_opline == opline) {
2937		||	zend_jit_use_last_valid_opline();
2938		|	cbnz TMP1w, ->interrupt_handler
2939	} else {
2940		|	cbnz TMP1w, >1
2941		|.cold_code
2942		|1:
2943		|	LOAD_IP_ADDR opline
2944		|	b ->interrupt_handler
2945		|.code
2946	}
2947	return 1;
2948}
2949
2950static int zend_jit_trace_end_loop(dasm_State **Dst, int loop_label, const void *timeout_exit_addr)
2951{
2952	if (timeout_exit_addr) {
2953		|	MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1
2954		|	cbz TMP1w, =>loop_label
2955		|	b &timeout_exit_addr
2956	} else {
2957		|	b =>loop_label
2958	}
2959	return 1;
2960}
2961
2962static int zend_jit_check_exception(dasm_State **Dst)
2963{
2964	|	MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1
2965	|	cbnz TMP2, ->exception_handler
2966	return 1;
2967}
2968
2969static int zend_jit_check_exception_undef_result(dasm_State **Dst, const zend_op *opline)
2970{
2971	if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
2972		|	MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1
2973		|	cbnz TMP2, ->exception_handler_undef
2974		return 1;
2975	}
2976	return zend_jit_check_exception(Dst);
2977}
2978
2979static int zend_jit_trace_begin(dasm_State **Dst, uint32_t trace_num, zend_jit_trace_info *parent, uint32_t exit_num)
2980{
2981
2982	current_trace_num = trace_num;
2983
2984	|	// EG(jit_trace_num) = trace_num;
2985	|	LOAD_32BIT_VAL TMP1w, trace_num
2986	|	MEM_STORE_32_ZTS str, TMP1w, executor_globals, jit_trace_num, TMP2
2987
2988	return 1;
2989}
2990
2991static int zend_jit_trace_end(dasm_State **Dst, zend_jit_trace_info *t)
2992{
2993	uint32_t i;
2994	const void *exit_addr;
2995
2996	/* Emit veneers table for exit points (B instruction for each exit number) */
2997	|.cold_code
2998	for (i = 0; i < t->exit_count; i++) {
2999		exit_addr = zend_jit_trace_get_exit_addr(i);
3000		if (!exit_addr) {
3001			return 0;
3002		}
3003		|	b &exit_addr
3004	}
3005	|=>1: // end of the code
3006	|.code
3007	return 1;
3008}
3009
3010static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size, const void *from_addr, const void *to_addr)
3011{
3012	int ret = 0;
3013	uint8_t *p, *end;
3014	const void *veneer = NULL;
3015	ptrdiff_t delta;
3016
3017	if (jmp_table_size) {
3018		const void **jmp_slot = (const void **)((char*)code + ZEND_MM_ALIGNED_SIZE_EX(size, sizeof(void*)));
3019
3020		do {
3021			if (*jmp_slot == from_addr) {
3022				*jmp_slot = to_addr;
3023				ret++;
3024			}
3025			jmp_slot++;
3026		} while (--jmp_table_size);
3027	}
3028
3029	end = (uint8_t*)code;
3030	p = end + size;
3031	while (p > end) {
3032		uint32_t *ins_ptr;
3033		uint32_t ins;
3034
3035		p -= 4;
3036		ins_ptr = (uint32_t*)p;
3037		ins = *ins_ptr;
3038		if ((ins & 0xfc000000u) == 0x14000000u) {
3039			// B (imm26:0..25)
3040			delta = (uint32_t*)from_addr - ins_ptr;
3041			if (((ins ^ (uint32_t)delta) & 0x01ffffffu) == 0) {
3042				delta = (uint32_t*)to_addr - ins_ptr;
3043				if (((delta + 0x02000000) >> 26) != 0) {
3044					abort(); // brnach target out of range
3045				}
3046				*ins_ptr = (ins & 0xfc000000u) | ((uint32_t)delta & 0x03ffffffu);
3047				ret++;
3048				if (!veneer) {
3049					veneer = p;
3050				}
3051			}
3052		} else if ((ins & 0xff000000u) == 0x54000000u ||
3053		           (ins & 0x7e000000u) == 0x34000000u) {
3054			// B.cond, CBZ, CBNZ (imm19:5..23)
3055			delta = (uint32_t*)from_addr - ins_ptr;
3056			if (((ins ^ ((uint32_t)delta << 5)) & 0x00ffffe0u) == 0) {
3057				delta = (uint32_t*)to_addr - ins_ptr;
3058				if (((delta + 0x40000) >> 19) != 0) {
3059					if (veneer) {
3060						delta = (uint32_t*)veneer - ins_ptr;
3061						if (((delta + 0x40000) >> 19) != 0) {
3062							abort(); // brnach target out of range
3063						}
3064					} else {
3065						abort(); // brnach target out of range
3066					}
3067				}
3068				*ins_ptr = (ins & 0xff00001fu) | (((uint32_t)delta & 0x7ffffu) << 5);
3069				ret++;
3070			}
3071		} else if ((ins & 0x7e000000u) == 0x36000000u) {
3072			// TBZ, TBNZ (imm14:5..18)
3073			delta = (uint32_t*)from_addr - ins_ptr;
3074			if (((ins ^ ((uint32_t)delta << 5)) & 0x0007ffe0u) == 0) {
3075				delta = (uint32_t*)to_addr - ins_ptr;
3076				if (((delta + 0x2000) >> 14) != 0) {
3077					if (veneer) {
3078						delta = (uint32_t*)veneer - ins_ptr;
3079						if (((delta + 0x2000) >> 14) != 0) {
3080							abort(); // brnach target out of range
3081						}
3082					} else {
3083						abort(); // brnach target out of range
3084					}
3085				}
3086				*ins_ptr = (ins & 0xfff8001fu) | (((uint32_t)delta & 0x3fffu) << 5);
3087				ret++;
3088			}
3089		}
3090	}
3091
3092	JIT_CACHE_FLUSH(code, (char*)code + size);
3093
3094#ifdef HAVE_VALGRIND
3095	VALGRIND_DISCARD_TRANSLATIONS(code, size);
3096#endif
3097
3098	return ret;
3099}
3100
3101static int zend_jit_link_side_trace(const void *code, size_t size, uint32_t jmp_table_size, uint32_t exit_num, const void *addr)
3102{
3103	return zend_jit_patch(code, size, jmp_table_size, zend_jit_trace_get_exit_addr(exit_num), addr);
3104}
3105
3106static int zend_jit_trace_link_to_root(dasm_State **Dst, zend_jit_trace_info *t, const void *timeout_exit_addr)
3107{
3108	const void *link_addr;
3109	size_t prologue_size;
3110
3111	/* Skip prologue. */
3112	// TODO: don't hardcode this ???
3113	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3114#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
3115		prologue_size = 0;
3116#else
3117		// sub sp, sp, #0x20
3118		prologue_size = 4;
3119#endif
3120	} else if (GCC_GLOBAL_REGS) {
3121		// stp x29, x30, [sp, # -SPAD]!
3122		prologue_size = 4;
3123	} else {
3124		// stp x29, x30, [sp, # -NR_SPAD]! // stack alignment
3125		// stp FP, RX, T2
3126		// mov FP, FCARG1x
3127		prologue_size = 12;
3128	}
3129	link_addr = (const void*)((const char*)t->code_start + prologue_size);
3130
3131	if (timeout_exit_addr) {
3132		/* Check timeout for links to LOOP */
3133		|	MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1
3134		|	cbz TMP1w, &link_addr
3135		|	b &timeout_exit_addr
3136	} else {
3137		|	b &link_addr
3138	}
3139	return 1;
3140}
3141
3142static int zend_jit_trace_return(dasm_State **Dst, bool original_handler, const zend_op *opline)
3143{
3144	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3145		|	ADD_HYBRID_SPAD
3146		if (!original_handler) {
3147			|	JMP_IP TMP1
3148		} else {
3149			|	ldr REG0, EX->func
3150			|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
3151			|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
3152			|	ldr REG0, [IP, REG0]
3153			|	br REG0
3154		}
3155	} else if (GCC_GLOBAL_REGS) {
3156		|	ldp x29, x30, [sp], # SPAD // stack alignment
3157		if (!original_handler) {
3158			|	JMP_IP TMP1
3159		} else {
3160			|	ldr REG0, EX->func
3161			|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
3162			|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
3163			|	ldr REG0, [IP, REG0]
3164			|	br REG0
3165		}
3166	} else {
3167		if (original_handler) {
3168			|	mov FCARG1x, FP
3169			|	ldr REG0, EX->func
3170			|	ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])]
3171			|	ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)]
3172			|	ldr REG0, [IP, REG0]
3173			|	blr REG0
3174		}
3175		|	ldp FP, RX, T2                // retore FP and IP
3176		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
3177		if (!original_handler || !opline ||
3178		    (opline->opcode != ZEND_RETURN
3179		  && opline->opcode != ZEND_RETURN_BY_REF
3180		  && opline->opcode != ZEND_GENERATOR_RETURN
3181		  && opline->opcode != ZEND_GENERATOR_CREATE
3182		  && opline->opcode != ZEND_YIELD
3183		  && opline->opcode != ZEND_YIELD_FROM)) {
3184			|	mov RETVALx, #2               // ZEND_VM_LEAVE
3185		}
3186		|	ret
3187	}
3188	return 1;
3189}
3190
3191static int zend_jit_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint8_t type)
3192{
3193	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
3194	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3195	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3196
3197	if (!exit_addr) {
3198		return 0;
3199	}
3200
3201	|	IF_NOT_ZVAL_TYPE var_addr, type, &exit_addr, ZREG_TMP1
3202
3203	return 1;
3204}
3205
3206static int zend_jit_scalar_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var)
3207{
3208	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
3209	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3210
3211	if (!exit_addr) {
3212		return 0;
3213	}
3214	|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP1w, FP, var+offsetof(zval, u1.v.type), TMP1
3215	|	cmp TMP1w, #IS_STRING
3216	|	bhs &exit_addr
3217
3218	return 1;
3219}
3220static int zend_jit_packed_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint32_t op_info)
3221{
3222	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
3223	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3224	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3225
3226	if (!exit_addr) {
3227		return 0;
3228	}
3229
3230	|	GET_ZVAL_LVAL ZREG_FCARG1, var_addr, TMP1
3231	if (op_info & MAY_BE_ARRAY_PACKED) {
3232		|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
3233		|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
3234		|	beq &exit_addr
3235	} else {
3236		|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
3237		|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
3238		|	bne &exit_addr
3239	}
3240
3241	return 1;
3242}
3243
3244static 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)
3245{
3246	zend_jit_op_array_trace_extension *jit_extension =
3247		(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
3248	size_t offset = jit_extension->offset;
3249	const void *handler =
3250		(zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler;
3251
3252	if (!zend_jit_set_valid_ip(Dst, opline)) {
3253		return 0;
3254	}
3255	if (!GCC_GLOBAL_REGS) {
3256		|	mov FCARG1x, FP
3257	}
3258	|	EXT_CALL handler, REG0
3259	if (may_throw
3260	 && opline->opcode != ZEND_RETURN
3261	 && opline->opcode != ZEND_RETURN_BY_REF) {
3262		|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
3263		|	cbnz REG0, ->exception_handler
3264	}
3265
3266	while (trace->op != ZEND_JIT_TRACE_VM && trace->op != ZEND_JIT_TRACE_END) {
3267		trace++;
3268	}
3269
3270	if (!GCC_GLOBAL_REGS
3271	 && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_RETURN)) {
3272		if (opline->opcode == ZEND_RETURN ||
3273		    opline->opcode == ZEND_RETURN_BY_REF ||
3274		    opline->opcode == ZEND_DO_UCALL ||
3275		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
3276		    opline->opcode == ZEND_DO_FCALL ||
3277		    opline->opcode == ZEND_GENERATOR_CREATE) {
3278			|	MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, TMP1
3279		}
3280	}
3281
3282	if (zend_jit_trace_may_exit(op_array, opline)) {
3283		if (opline->opcode == ZEND_RETURN ||
3284		    opline->opcode == ZEND_RETURN_BY_REF ||
3285		    opline->opcode == ZEND_GENERATOR_CREATE) {
3286
3287			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3288#if 0
3289				/* this check should be handled by the following OPLINE guard or jmp [IP] */
3290				|	LOAD_ADDR TMP1, zend_jit_halt_op
3291				|	cmp IP, TMP1
3292				|	beq ->trace_halt
3293#endif
3294			} else if (GCC_GLOBAL_REGS) {
3295				|	cbz IP, ->trace_halt
3296			} else {
3297				|	tst RETVALw, RETVALw
3298				|	blt ->trace_halt
3299			}
3300		} else if (opline->opcode == ZEND_EXIT ||
3301		           opline->opcode == ZEND_GENERATOR_RETURN ||
3302		           opline->opcode == ZEND_YIELD ||
3303		           opline->opcode == ZEND_YIELD_FROM) {
3304			|	b ->trace_halt
3305		}
3306		if (trace->op != ZEND_JIT_TRACE_END ||
3307		    (trace->stop != ZEND_JIT_TRACE_STOP_RETURN &&
3308		     trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
3309
3310			const zend_op *next_opline = trace->opline;
3311			const zend_op *exit_opline = NULL;
3312			uint32_t exit_point;
3313			const void *exit_addr;
3314			uint32_t old_info = 0;
3315			uint32_t old_res_info = 0;
3316			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
3317
3318			if (zend_is_smart_branch(opline)) {
3319				bool exit_if_true = 0;
3320				exit_opline = zend_jit_trace_get_exit_opline(trace, opline + 1, &exit_if_true);
3321			} else {
3322				switch (opline->opcode) {
3323					case ZEND_JMPZ:
3324					case ZEND_JMPNZ:
3325					case ZEND_JMPZ_EX:
3326					case ZEND_JMPNZ_EX:
3327					case ZEND_JMP_SET:
3328					case ZEND_COALESCE:
3329					case ZEND_JMP_NULL:
3330					case ZEND_FE_RESET_R:
3331					case ZEND_FE_RESET_RW:
3332						exit_opline = (trace->opline == opline + 1) ?
3333							OP_JMP_ADDR(opline, opline->op2) :
3334							opline + 1;
3335						break;
3336					case ZEND_JMPZNZ:
3337						exit_opline = (trace->opline == OP_JMP_ADDR(opline, opline->op2)) ?
3338							ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
3339							OP_JMP_ADDR(opline, opline->op2);
3340						break;
3341					case ZEND_FE_FETCH_R:
3342					case ZEND_FE_FETCH_RW:
3343						exit_opline = (trace->opline == opline + 1) ?
3344							ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
3345							opline + 1;
3346						break;
3347
3348				}
3349			}
3350
3351			switch (opline->opcode) {
3352				case ZEND_FE_FETCH_R:
3353				case ZEND_FE_FETCH_RW:
3354					if (opline->op2_type != IS_UNUSED) {
3355						old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var));
3356						SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1);
3357					}
3358					break;
3359			}
3360
3361			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
3362				old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
3363				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
3364			}
3365			exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
3366			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3367
3368			if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) {
3369				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
3370			}
3371			switch (opline->opcode) {
3372				case ZEND_FE_FETCH_R:
3373				case ZEND_FE_FETCH_RW:
3374					if (opline->op2_type != IS_UNUSED) {
3375						SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var), old_info);
3376					}
3377					break;
3378			}
3379
3380			if (!exit_addr) {
3381				return 0;
3382			}
3383			|	CMP_IP next_opline, TMP1, TMP2
3384			|	bne &exit_addr
3385		}
3386	}
3387
3388	zend_jit_set_last_valid_opline(trace->opline);
3389
3390	return 1;
3391}
3392
3393static int zend_jit_handler(dasm_State **Dst, const zend_op *opline, int may_throw)
3394{
3395	const void *handler;
3396
3397	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3398		handler = zend_get_opcode_handler_func(opline);
3399	} else {
3400		handler = opline->handler;
3401	}
3402
3403	if (!zend_jit_set_valid_ip(Dst, opline)) {
3404		return 0;
3405	}
3406	if (!GCC_GLOBAL_REGS) {
3407		|	mov FCARG1x, FP
3408	}
3409	|	EXT_CALL handler, REG0
3410	if (may_throw) {
3411		zend_jit_check_exception(Dst);
3412	}
3413
3414	/* Skip the following OP_DATA */
3415	switch (opline->opcode) {
3416		case ZEND_ASSIGN_DIM:
3417		case ZEND_ASSIGN_OBJ:
3418		case ZEND_ASSIGN_STATIC_PROP:
3419		case ZEND_ASSIGN_DIM_OP:
3420		case ZEND_ASSIGN_OBJ_OP:
3421		case ZEND_ASSIGN_STATIC_PROP_OP:
3422		case ZEND_ASSIGN_STATIC_PROP_REF:
3423		case ZEND_ASSIGN_OBJ_REF:
3424			zend_jit_set_last_valid_opline(opline + 2);
3425			break;
3426		default:
3427			zend_jit_set_last_valid_opline(opline + 1);
3428			break;
3429	}
3430
3431	return 1;
3432}
3433
3434static int zend_jit_tail_handler(dasm_State **Dst, const zend_op *opline)
3435{
3436	if (!zend_jit_set_valid_ip(Dst, opline)) {
3437		return 0;
3438	}
3439	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
3440		if (opline->opcode == ZEND_DO_UCALL ||
3441		    opline->opcode == ZEND_DO_FCALL_BY_NAME ||
3442		    opline->opcode == ZEND_DO_FCALL ||
3443		    opline->opcode == ZEND_RETURN) {
3444
3445			/* Use inlined HYBRID VM handler */
3446			const void *handler = opline->handler;
3447
3448			|	ADD_HYBRID_SPAD
3449			|	EXT_JMP handler, REG0
3450		} else {
3451			const void *handler = zend_get_opcode_handler_func(opline);
3452
3453			|	EXT_CALL handler, REG0
3454			|	ADD_HYBRID_SPAD
3455			|	JMP_IP TMP1
3456		}
3457	} else {
3458		const void *handler = opline->handler;
3459
3460		if (GCC_GLOBAL_REGS) {
3461			|	ldp x29, x30, [sp], # SPAD // stack alignment
3462		} else {
3463			|	mov FCARG1x, FP
3464			|	ldp FP, RX, T2                // retore FP and IP
3465			|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
3466		}
3467		|	EXT_JMP handler, REG0
3468	}
3469	zend_jit_reset_last_valid_opline();
3470	return 1;
3471}
3472
3473static int zend_jit_trace_opline_guard(dasm_State **Dst, const zend_op *opline)
3474{
3475	uint32_t exit_point = zend_jit_trace_get_exit_point(NULL, 0);
3476	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3477
3478	if (!exit_addr) {
3479		return 0;
3480	}
3481	|	CMP_IP opline, TMP1, TMP2
3482	|	bne &exit_addr
3483
3484	zend_jit_set_last_valid_opline(opline);
3485
3486	return 1;
3487}
3488
3489static int zend_jit_jmp(dasm_State **Dst, unsigned int target_label)
3490{
3491	|	b =>target_label
3492	return 1;
3493}
3494
3495static int zend_jit_cond_jmp(dasm_State **Dst, const zend_op *next_opline, unsigned int target_label)
3496{
3497	|	CMP_IP next_opline, TMP1, TMP2
3498	|	bne =>target_label
3499
3500	zend_jit_set_last_valid_opline(next_opline);
3501
3502	return 1;
3503}
3504
3505#ifdef CONTEXT_THREADED_JIT
3506static int zend_jit_context_threaded_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
3507{
3508	|	NIY	// TODO
3509	return 1;
3510}
3511#endif
3512
3513static int zend_jit_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block)
3514{
3515#ifdef CONTEXT_THREADED_JIT
3516	return zend_jit_context_threaded_call(Dst, opline, next_block);
3517#else
3518	return zend_jit_tail_handler(Dst, opline);
3519#endif
3520}
3521
3522static int zend_jit_spill_store(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info, bool set_type)
3523{
3524	ZEND_ASSERT(Z_MODE(src) == IS_REG);
3525	ZEND_ASSERT(Z_MODE(dst) == IS_MEM_ZVAL);
3526
3527	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3528		|	SET_ZVAL_LVAL_FROM_REG dst, Rx(Z_REG(src)), TMP1
3529		if (set_type &&
3530		    (Z_REG(dst) != ZREG_FP ||
3531		     !JIT_G(current_frame) ||
3532		     STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_LONG)) {
3533			|	SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2
3534		}
3535	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3536		|	SET_ZVAL_DVAL dst, Z_REG(src), ZREG_TMP1
3537		if (set_type &&
3538		    (Z_REG(dst) != ZREG_FP ||
3539		     !JIT_G(current_frame) ||
3540		     STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_DOUBLE)) {
3541			|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2
3542		}
3543	} else {
3544		ZEND_UNREACHABLE();
3545	}
3546	return 1;
3547}
3548
3549static int zend_jit_load_reg(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
3550{
3551	ZEND_ASSERT(Z_MODE(src) == IS_MEM_ZVAL);
3552	ZEND_ASSERT(Z_MODE(dst) == IS_REG);
3553
3554	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3555		|	GET_ZVAL_LVAL Z_REG(dst), src, TMP1
3556	} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3557		|	GET_ZVAL_DVAL Z_REG(dst), src, ZREG_TMP1
3558	} else {
3559		ZEND_UNREACHABLE();
3560	}
3561	return 1;
3562}
3563
3564static int zend_jit_store_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg, bool set_type)
3565{
3566	zend_jit_addr src = ZEND_ADDR_REG(reg);
3567	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3568
3569	return zend_jit_spill_store(Dst, src, dst, info, set_type);
3570}
3571
3572static int zend_jit_store_var_type(dasm_State **Dst, int var, uint32_t type)
3573{
3574	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3575
3576	|	SET_ZVAL_TYPE_INFO dst, type, TMP1w, TMP2
3577	return 1;
3578}
3579
3580static int zend_jit_store_var_if_necessary(dasm_State **Dst, int var, zend_jit_addr src, uint32_t info)
3581{
3582	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
3583		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3584		return zend_jit_spill_store(Dst, src, dst, info, 1);
3585	}
3586	return 1;
3587}
3588
3589static 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)
3590{
3591	if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
3592		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3593		bool set_type = 1;
3594
3595		if ((info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) ==
3596		    (old_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF))) {
3597			if (Z_MODE(old) != IS_REG || Z_LOAD(old) || Z_STORE(old)) {
3598				set_type = 0;
3599			}
3600		}
3601		return zend_jit_spill_store(Dst, src, dst, info, set_type);
3602	}
3603	return 1;
3604}
3605
3606static int zend_jit_load_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg)
3607{
3608	zend_jit_addr src = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3609	zend_jit_addr dst = ZEND_ADDR_REG(reg);
3610
3611	return zend_jit_load_reg(Dst, src, dst, info);
3612}
3613
3614static int zend_jit_invalidate_var_if_necessary(dasm_State **Dst, zend_uchar op_type, zend_jit_addr addr, znode_op op)
3615{
3616	if ((op_type & (IS_TMP_VAR|IS_VAR)) && Z_MODE(addr) == IS_REG && !Z_LOAD(addr) && !Z_STORE(addr)) {
3617		zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var);
3618		|	SET_ZVAL_TYPE_INFO dst, IS_UNDEF, TMP1w, TMP2
3619	}
3620	return 1;
3621}
3622
3623static int zend_jit_update_regs(dasm_State **Dst, uint32_t var, zend_jit_addr src, zend_jit_addr dst, uint32_t info)
3624{
3625	if (!zend_jit_same_addr(src, dst)) {
3626		if (Z_MODE(src) == IS_REG) {
3627			if (Z_MODE(dst) == IS_REG) {
3628				if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
3629					|	mov Rx(Z_REG(dst)), Rx(Z_REG(src))
3630				} else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
3631					|	fmov Rd(Z_REG(dst)-ZREG_V0), Rd(Z_REG(src)-ZREG_V0)
3632				} else {
3633					ZEND_UNREACHABLE();
3634				}
3635				if (!Z_LOAD(src) && !Z_STORE(src) && Z_STORE(dst)) {
3636					zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3637
3638					if (!zend_jit_spill_store(Dst, dst, var_addr, info,
3639							JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3640							JIT_G(current_frame) == NULL ||
3641							STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3642							(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3643					)) {
3644						return 0;
3645					}
3646				}
3647			} else if (Z_MODE(dst) == IS_MEM_ZVAL) {
3648				if (!Z_LOAD(src) && !Z_STORE(src)) {
3649					if (!zend_jit_spill_store(Dst, src, dst, info,
3650							JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3651							JIT_G(current_frame) == NULL ||
3652							STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3653							(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3654					)) {
3655						return 0;
3656					}
3657				}
3658			} else {
3659				ZEND_UNREACHABLE();
3660			}
3661		} else if (Z_MODE(src) == IS_MEM_ZVAL) {
3662			if (Z_MODE(dst) == IS_REG) {
3663				if (!zend_jit_load_reg(Dst, src, dst, info)) {
3664					return 0;
3665				}
3666			} else {
3667				ZEND_UNREACHABLE();
3668			}
3669		} else {
3670			ZEND_UNREACHABLE();
3671		}
3672	} else if (Z_MODE(dst) == IS_REG && Z_STORE(dst)) {
3673		dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
3674		if (!zend_jit_spill_store(Dst, src, dst, info,
3675				JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
3676				JIT_G(current_frame) == NULL ||
3677				STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN ||
3678				(1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY)
3679		)) {
3680			return 0;
3681		}
3682	}
3683	return 1;
3684}
3685
3686static int zend_jit_escape_if_undef_r0(dasm_State **Dst, int var, uint32_t flags, const zend_op *opline)
3687{
3688	zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
3689
3690	|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1
3691
3692	if (flags & ZEND_JIT_EXIT_RESTORE_CALL) {
3693		if (!zend_jit_save_call_chain(Dst, -1)) {
3694			return 0;
3695		}
3696	}
3697
3698	ZEND_ASSERT(opline);
3699
3700	if ((opline-1)->opcode != ZEND_FETCH_CONSTANT
3701	 && (opline-1)->opcode != ZEND_FETCH_LIST_R
3702	 && ((opline-1)->op1_type & (IS_VAR|IS_TMP_VAR))
3703	 && !(flags & ZEND_JIT_EXIT_FREE_OP1)) {
3704		val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline-1)->op1.var);
3705
3706		|	IF_NOT_ZVAL_REFCOUNTED val_addr, >2, ZREG_TMP1, ZREG_TMP2
3707		|	GET_ZVAL_PTR TMP1, val_addr, TMP2
3708		|	GC_ADDREF TMP1, TMP2w
3709		|2:
3710	}
3711
3712	|	LOAD_IP_ADDR (opline - 1)
3713	|	b ->trace_escape
3714	|1:
3715
3716	return 1;
3717}
3718
3719static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
3720{
3721	zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
3722
3723	if (reg == ZREG_LONG_MIN_MINUS_1) {
3724		uint64_t val = 0xc3e0000000000000;
3725		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
3726		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2
3727	} else if (reg == ZREG_LONG_MIN) {
3728		uint64_t val = 0x8000000000000000;
3729		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
3730		|	SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2
3731	} else if (reg == ZREG_LONG_MAX) {
3732		uint64_t val = 0x7fffffffffffffff;
3733		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
3734		|	SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2
3735	} else if (reg == ZREG_LONG_MAX_PLUS_1) {
3736		uint64_t val = 0x43e0000000000000;
3737		|	SET_ZVAL_LVAL dst, val, TMP1, TMP2
3738		|	SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2
3739	} else if (reg == ZREG_NULL) {
3740		|	SET_ZVAL_TYPE_INFO dst, IS_NULL, TMP1w, TMP2
3741	} else if (reg == ZREG_ZVAL_TRY_ADDREF) {
3742		|	IF_NOT_ZVAL_REFCOUNTED dst, >1, ZREG_TMP1, ZREG_TMP2
3743		|	GET_ZVAL_PTR TMP1, dst, TMP2
3744		|	GC_ADDREF TMP1, TMP2w
3745		|1:
3746	} else if (reg == ZREG_ZVAL_COPY_GPR0) {
3747		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
3748
3749		|	ZVAL_COPY_VALUE dst, -1, val_addr, -1, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3750		|	TRY_ADDREF -1, REG1w, REG2, TMP1w
3751	} else {
3752		ZEND_UNREACHABLE();
3753	}
3754	return 1;
3755}
3756
3757static int zend_jit_free_trampoline(dasm_State **Dst)
3758{
3759	|	// if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
3760	|	ldr TMP1w, [REG0, #offsetof(zend_function, common.fn_flags)]
3761	|	TST_32_WITH_CONST TMP1w, ZEND_ACC_CALL_VIA_TRAMPOLINE, TMP2w
3762	|	beq >1
3763	|	mov FCARG1x, REG0
3764	|	EXT_CALL zend_jit_free_trampoline_helper, REG0
3765	|1:
3766	return 1;
3767}
3768
3769static 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)
3770{
3771	if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_LONG)) {
3772		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2, ZREG_TMP1
3773	}
3774	if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
3775		|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3776	}
3777	if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, MAY_BE_LONG)) {
3778		return 0;
3779	}
3780	if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
3781		|	LONG_ADD_SUB_WITH_IMM adds, op1_def_addr, Z_L(1), TMP1, TMP2
3782	} else {
3783		|	LONG_ADD_SUB_WITH_IMM subs, op1_def_addr, Z_L(1), TMP1, TMP2
3784	}
3785
3786	if (may_overflow &&
3787	    (((op1_def_info & MAY_BE_GUARD) && (op1_def_info & MAY_BE_LONG)) ||
3788	     ((opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD) && (res_info & MAY_BE_LONG))))) {
3789		int32_t exit_point;
3790		const void *exit_addr;
3791		zend_jit_trace_stack *stack;
3792		uint32_t old_op1_info, old_res_info = 0;
3793
3794		stack = JIT_G(current_frame)->stack;
3795		old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
3796		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_DOUBLE, 0);
3797		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
3798			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MAX_PLUS_1);
3799		} else {
3800			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MIN_MINUS_1);
3801		}
3802		if (opline->result_type != IS_UNUSED) {
3803			old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
3804			if (opline->opcode == ZEND_PRE_INC) {
3805				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
3806				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX_PLUS_1);
3807			} else if (opline->opcode == ZEND_PRE_DEC) {
3808				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
3809				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN_MINUS_1);
3810			} else if (opline->opcode == ZEND_POST_INC) {
3811				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
3812				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX);
3813			} else if (opline->opcode == ZEND_POST_DEC) {
3814				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
3815				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN);
3816			}
3817		}
3818
3819		exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
3820		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3821		if (!exit_addr) {
3822			return 0;
3823		}
3824		|	bvs &exit_addr
3825
3826		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
3827		    opline->result_type != IS_UNUSED) {
3828			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3829		}
3830
3831		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
3832		if (opline->result_type != IS_UNUSED) {
3833			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
3834		}
3835	} else if (may_overflow) {
3836		|	bvs >1
3837		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
3838		    opline->result_type != IS_UNUSED) {
3839			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3840		}
3841		|.cold_code
3842		|1:
3843		if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
3844			uint64_t val = 0x43e0000000000000;
3845			if (Z_MODE(op1_def_addr) == IS_REG) {
3846				|	LOAD_64BIT_VAL TMP1, val
3847				|	fmov Rd(Z_REG(op1_def_addr)-ZREG_V0), TMP1
3848			} else {
3849				|	SET_ZVAL_LVAL op1_def_addr, val, TMP2, TMP1
3850			}
3851		} else {
3852			uint64_t val = 0xc3e0000000000000;
3853			if (Z_MODE(op1_def_addr) == IS_REG) {
3854				|	LOAD_64BIT_VAL TMP1, val
3855				|	fmov Rd(Z_REG(op1_def_addr)-ZREG_V0), TMP1
3856			} else {
3857				|	SET_ZVAL_LVAL op1_def_addr, val, TMP2, TMP1
3858			}
3859		}
3860		if (Z_MODE(op1_def_addr) == IS_MEM_ZVAL) {
3861			|	SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE, TMP1w, TMP2
3862		}
3863		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
3864		    opline->result_type != IS_UNUSED) {
3865			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_DOUBLE, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3866		}
3867		|	b >3
3868		|.code
3869	} else {
3870		if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
3871		    opline->result_type != IS_UNUSED) {
3872			|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3873		}
3874	}
3875	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
3876		|.cold_code
3877		|2:
3878		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
3879			|	SET_EX_OPLINE opline, REG0
3880			if (op1_info & MAY_BE_UNDEF) {
3881				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >2, ZREG_TMP1
3882				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
3883				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
3884				|	EXT_CALL zend_jit_undefined_op_helper, REG0
3885				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2
3886				op1_info |= MAY_BE_NULL;
3887			}
3888			|2:
3889			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
3890
3891			|	// ZVAL_DEREF(var_ptr);
3892			if (op1_info & MAY_BE_REF) {
3893				|	IF_NOT_Z_TYPE, FCARG1x, IS_REFERENCE, >2, TMP1w
3894				|	GET_Z_PTR FCARG1x, FCARG1x
3895				|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
3896				|	cbz TMP1, >1
3897				if (RETURN_VALUE_USED(opline)) {
3898					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
3899				} else {
3900					|	mov FCARG2x, xzr
3901				}
3902				if (opline->opcode == ZEND_PRE_INC) {
3903					|	EXT_CALL zend_jit_pre_inc_typed_ref, REG0
3904				} else if (opline->opcode == ZEND_PRE_DEC) {
3905					|	EXT_CALL zend_jit_pre_dec_typed_ref, REG0
3906				} else if (opline->opcode == ZEND_POST_INC) {
3907					|	EXT_CALL zend_jit_post_inc_typed_ref, REG0
3908				} else if (opline->opcode == ZEND_POST_DEC) {
3909					|	EXT_CALL zend_jit_post_dec_typed_ref, REG0
3910				} else {
3911					ZEND_UNREACHABLE();
3912				}
3913				zend_jit_check_exception(Dst);
3914				|	b >3
3915				|1:
3916				|	add FCARG1x, FCARG1x, #offsetof(zend_reference, val)
3917				|2:
3918			}
3919
3920			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
3921				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
3922
3923				|	ZVAL_COPY_VALUE res_addr, res_use_info, val_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3924				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
3925			}
3926			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
3927				if (opline->opcode == ZEND_PRE_INC && opline->result_type != IS_UNUSED) {
3928					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
3929					|	EXT_CALL zend_jit_pre_inc, REG0
3930				} else {
3931					|	EXT_CALL increment_function, REG0
3932				}
3933			} else {
3934				if (opline->opcode == ZEND_PRE_DEC && opline->result_type != IS_UNUSED) {
3935					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
3936					|	EXT_CALL zend_jit_pre_dec, REG0
3937				} else {
3938					|	EXT_CALL decrement_function, REG0
3939				}
3940			}
3941			if (may_throw) {
3942				zend_jit_check_exception(Dst);
3943			}
3944		} else {
3945			zend_reg tmp_reg;
3946
3947			if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
3948				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_DOUBLE, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3949			}
3950			if (Z_MODE(op1_def_addr) == IS_REG) {
3951				tmp_reg = Z_REG(op1_def_addr);
3952			} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
3953				tmp_reg = Z_REG(op1_addr);
3954			} else {
3955				tmp_reg = ZREG_FPR0;
3956			}
3957			|	GET_ZVAL_DVAL tmp_reg, op1_addr, ZREG_TMP1
3958			if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
3959				uint64_t val = 0x3ff0000000000000; // 1.0
3960				|	LOAD_64BIT_VAL TMP1, val
3961				|	fmov FPTMP, TMP1
3962				|	fadd Rd(tmp_reg-ZREG_V0), Rd(tmp_reg-ZREG_V0), FPTMP
3963			} else {
3964				uint64_t val = 0x3ff0000000000000; // 1.0
3965				|	LOAD_64BIT_VAL TMP1, val
3966				|	fmov FPTMP, TMP1
3967				|	fsub Rd(tmp_reg-ZREG_V0), Rd(tmp_reg-ZREG_V0), FPTMP
3968			}
3969			|	SET_ZVAL_DVAL op1_def_addr, tmp_reg, ZREG_TMP1
3970			if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
3971			    opline->result_type != IS_UNUSED) {
3972				|	ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, op1_def_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
3973				|	TRY_ADDREF op1_def_info, REG0w, REG1, TMP1w
3974			}
3975		}
3976		|	b >3
3977		|.code
3978	}
3979	|3:
3980	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_def_addr, op1_def_info, op1_addr, op1_info)) {
3981		return 0;
3982	}
3983	if (opline->result_type != IS_UNUSED) {
3984		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
3985			return 0;
3986		}
3987	}
3988	return 1;
3989}
3990
3991static int zend_jit_opline_uses_reg(const zend_op  *opline, int8_t reg)
3992{
3993	if ((opline+1)->opcode == ZEND_OP_DATA
3994	 && ((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV))
3995	 && JIT_G(current_frame)->stack[EX_VAR_TO_NUM((opline+1)->op1.var)].reg == reg) {
3996		return 1;
3997	}
3998	return
3999		((opline->result_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4000			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->result.var)].reg == reg) ||
4001		((opline->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4002			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op1.var)].reg == reg) ||
4003		((opline->op2_type & (IS_VAR|IS_TMP_VAR|IS_CV)) &&
4004			JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op2.var)].reg == reg);
4005}
4006
4007static int zend_jit_math_long_long(dasm_State    **Dst,
4008                                   const zend_op  *opline,
4009                                   zend_uchar      opcode,
4010                                   zend_jit_addr   op1_addr,
4011                                   zend_jit_addr   op2_addr,
4012                                   zend_jit_addr   res_addr,
4013                                   uint32_t        res_info,
4014                                   uint32_t        res_use_info,
4015                                   int             may_overflow)
4016{
4017	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4018	zend_reg result_reg;
4019	zend_reg tmp_reg = ZREG_REG0;
4020	bool use_ovf_flag = 1;
4021
4022	if (Z_MODE(res_addr) == IS_REG && (res_info & MAY_BE_LONG)) {
4023		if (may_overflow && (res_info & MAY_BE_GUARD)
4024		 && JIT_G(current_frame)
4025		 && zend_jit_opline_uses_reg(opline, Z_REG(res_addr))) {
4026			result_reg = ZREG_REG0;
4027		} else {
4028			result_reg = Z_REG(res_addr);
4029		}
4030	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr) && !may_overflow) {
4031		result_reg = Z_REG(op1_addr);
4032	} else if (Z_REG(res_addr) != ZREG_REG0) {
4033		result_reg = ZREG_REG0;
4034	} else {
4035		/* ASSIGN_DIM_OP */
4036		result_reg = ZREG_FCARG1;
4037		tmp_reg = ZREG_FCARG1;
4038	}
4039
4040	if (opcode == ZEND_MUL &&
4041			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4042			Z_LVAL_P(Z_ZV(op2_addr)) == 2) {
4043		if (Z_MODE(op1_addr) == IS_REG) {
4044			|	adds Rx(result_reg), Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr))
4045		} else {
4046			|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4047			|	adds Rx(result_reg), Rx(result_reg), Rx(result_reg)
4048		}
4049	} else if (opcode == ZEND_MUL &&
4050			Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4051			!may_overflow &&
4052			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr)))) {
4053		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4054		|	mov TMP1, #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
4055		|	lsl Rx(result_reg), Rx(result_reg), TMP1
4056	} else if (opcode == ZEND_MUL &&
4057			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4058			Z_LVAL_P(Z_ZV(op1_addr)) == 2) {
4059		if (Z_MODE(op2_addr) == IS_REG) {
4060			|	adds Rx(result_reg), Rx(Z_REG(op2_addr)), Rx(Z_REG(op2_addr))
4061		} else {
4062			|	GET_ZVAL_LVAL result_reg, op2_addr, TMP1
4063			|	adds Rx(result_reg), Rx(result_reg), Rx(result_reg)
4064		}
4065	} else if (opcode == ZEND_MUL &&
4066			Z_MODE(op1_addr) == IS_CONST_ZVAL &&
4067			!may_overflow &&
4068			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op1_addr)))) {
4069		|	GET_ZVAL_LVAL result_reg, op2_addr, TMP1
4070		|	mov TMP1, #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
4071		|	lsl Rx(result_reg), Rx(result_reg), TMP1
4072	} else if (opcode == ZEND_DIV &&
4073			(Z_MODE(op2_addr) == IS_CONST_ZVAL &&
4074			zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr))))) {
4075		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4076		|	asr Rx(result_reg), Rx(result_reg), #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
4077#if 0
4078	/* x86 specific optimizations through LEA instraction are not supported on ARM */
4079	} else if (opcode == ZEND_ADD &&
4080			!may_overflow &&
4081			Z_MODE(op1_addr) == IS_REG &&
4082			Z_MODE(op2_addr) == IS_CONST_ZVAL) {
4083		|	NIY	// TODO: test
4084	} else if (opcode == ZEND_ADD &&
4085			!may_overflow &&
4086			Z_MODE(op2_addr) == IS_REG &&
4087			Z_MODE(op1_addr) == IS_CONST_ZVAL) {
4088		|	NIY	// TODO: test
4089	} else if (opcode == ZEND_SUB &&
4090			!may_overflow &&
4091			Z_MODE(op1_addr) == IS_REG &&
4092			Z_MODE(op2_addr) == IS_CONST_ZVAL) {
4093		|	NIY	// TODO: test
4094#endif
4095	} else if (opcode == ZEND_MUL) {
4096		|	GET_ZVAL_LVAL ZREG_TMP1, op1_addr, TMP1
4097		|	GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2
4098		|	mul Rx(result_reg), TMP1, TMP2
4099		if(may_overflow) {
4100			/* Use 'smulh' to get the upper 64 bits fo the 128-bit result.
4101			 * For signed multiplication, the top 65 bits of the result will contain
4102			 * either all zeros or all ones if no overflow occurred.
4103			 * Flag: bne -> overflow. beq -> no overflow.
4104			 */
4105			use_ovf_flag = 0;
4106			|	smulh TMP1, TMP1, TMP2
4107			|	cmp TMP1, Rx(result_reg), asr #63
4108		}
4109	} else {
4110		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4111		if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4112		 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4113		 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4114			/* +/- 0 */
4115			may_overflow = 0;
4116		} else if (same_ops && opcode != ZEND_DIV) {
4117			|	LONG_MATH_REG opcode, Rx(result_reg), Rx(result_reg), Rx(result_reg)
4118		} else {
4119			|	LONG_MATH opcode, result_reg, op2_addr, TMP1
4120		}
4121	}
4122	if (may_overflow) {
4123		if (res_info & MAY_BE_GUARD) {
4124			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
4125			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
4126			if (!exit_addr) {
4127				return 0;
4128			}
4129			if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) {
4130				if (use_ovf_flag) {
4131					|	bvs &exit_addr
4132				} else {
4133					|	bne &exit_addr
4134				}
4135				if (Z_MODE(res_addr) == IS_REG && result_reg != Z_REG(res_addr)) {
4136					|	mov Rx(Z_REG(res_addr)), Rx(result_reg)
4137				}
4138			} else if ((res_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
4139				if (use_ovf_flag) {
4140					|	bvc &exit_addr
4141				} else {
4142					|	beq &exit_addr
4143				}
4144			} else {
4145				ZEND_UNREACHABLE();
4146			}
4147		} else {
4148			if (res_info & MAY_BE_LONG) {
4149				if (use_ovf_flag) {
4150					|	bvs >1
4151				} else {
4152					|	bne >1
4153				}
4154			} else {
4155				if (use_ovf_flag) {
4156					|	bvc >1
4157				} else {
4158					|	beq >1
4159				}
4160			}
4161		}
4162	}
4163
4164	if (Z_MODE(res_addr) == IS_MEM_ZVAL && (res_info & MAY_BE_LONG)) {
4165		|	SET_ZVAL_LVAL_FROM_REG res_addr, Rx(result_reg), TMP1
4166		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4167			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
4168				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
4169			}
4170		}
4171	}
4172
4173	if (may_overflow && (!(res_info & MAY_BE_GUARD) || (res_info & MAY_BE_ANY) == MAY_BE_DOUBLE)) {
4174		zend_reg tmp_reg1 = ZREG_FPR0;
4175		zend_reg tmp_reg2 = ZREG_FPR1;
4176
4177		if (res_info & MAY_BE_LONG) {
4178			|.cold_code
4179			|1:
4180		}
4181
4182		do {
4183			if ((Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 1) ||
4184			    (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 1)) {
4185				if (opcode == ZEND_ADD) {
4186					uint64_t val = 0x43e0000000000000;
4187					if (Z_MODE(res_addr) == IS_REG) {
4188						|	LOAD_64BIT_VAL TMP1, val
4189						|	fmov Rd(Z_REG(res_addr)-ZREG_V0), TMP1
4190					} else {
4191						|	SET_ZVAL_LVAL res_addr, val, TMP2, TMP1
4192					}
4193					break;
4194				} else if (opcode == ZEND_SUB) {
4195					uint64_t val = 0xc3e0000000000000;
4196					if (Z_MODE(res_addr) == IS_REG) {
4197						|	LOAD_64BIT_VAL TMP1, val
4198						|	fmov Rd(Z_REG(res_addr)-ZREG_V0), TMP1
4199					} else {
4200						|	SET_ZVAL_LVAL res_addr, val, TMP2, TMP1
4201					}
4202					break;
4203				}
4204			}
4205
4206			|	DOUBLE_GET_ZVAL_LVAL tmp_reg1, op1_addr, tmp_reg, ZREG_TMP1
4207			|	DOUBLE_GET_ZVAL_LVAL tmp_reg2, op2_addr, tmp_reg, ZREG_TMP1
4208			|	DOUBLE_MATH_REG opcode, tmp_reg1, tmp_reg1, tmp_reg2
4209			|	SET_ZVAL_DVAL res_addr, tmp_reg1, ZREG_TMP1
4210		} while (0);
4211
4212		if (Z_MODE(res_addr) == IS_MEM_ZVAL
4213		 && (res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4214			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
4215		}
4216		if (res_info & MAY_BE_LONG) {
4217			|	b >2
4218			|.code
4219		}
4220		|2:
4221	}
4222
4223	return 1;
4224}
4225
4226static int zend_jit_math_long_double(dasm_State    **Dst,
4227                                     zend_uchar      opcode,
4228                                     zend_jit_addr   op1_addr,
4229                                     zend_jit_addr   op2_addr,
4230                                     zend_jit_addr   res_addr,
4231                                     uint32_t        res_use_info)
4232{
4233	zend_reg result_reg =
4234		(Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : ZREG_FPR0;
4235	zend_reg op2_reg;
4236
4237	|	DOUBLE_GET_ZVAL_LVAL result_reg, op1_addr, ZREG_TMP1, ZREG_TMP2
4238
4239	if (Z_MODE(op2_addr) == IS_REG) {
4240		op2_reg = Z_REG(op2_addr);
4241	} else {
4242		op2_reg = ZREG_FPTMP;
4243		|	GET_ZVAL_DVAL op2_reg, op2_addr, ZREG_TMP1
4244	}
4245
4246	|	DOUBLE_MATH_REG opcode, result_reg, result_reg, op2_reg
4247
4248	|	SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1
4249
4250	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4251		if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4252			|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
4253		}
4254	}
4255
4256	return 1;
4257}
4258
4259static int zend_jit_math_double_long(dasm_State    **Dst,
4260                                     zend_uchar      opcode,
4261                                     zend_jit_addr   op1_addr,
4262                                     zend_jit_addr   op2_addr,
4263                                     zend_jit_addr   res_addr,
4264                                     uint32_t        res_use_info)
4265{
4266	zend_reg result_reg, op1_reg, op2_reg;
4267
4268	if (zend_is_commutative(opcode)
4269	 && (Z_MODE(res_addr) != IS_REG || Z_MODE(op1_addr) != IS_REG || Z_REG(res_addr) != Z_REG(op1_addr))) {
4270		if (Z_MODE(res_addr) == IS_REG) {
4271			result_reg = Z_REG(res_addr);
4272		} else {
4273			result_reg = ZREG_FPR0;
4274		}
4275		|	DOUBLE_GET_ZVAL_LVAL result_reg, op2_addr, ZREG_TMP1, ZREG_TMP2
4276		if (Z_MODE(op1_addr) == IS_REG) {
4277			op1_reg = Z_REG(op1_addr);
4278		} else {
4279			op1_reg = ZREG_FPTMP;
4280			|	GET_ZVAL_DVAL op1_reg, op1_addr, ZREG_TMP1
4281		}
4282		|	DOUBLE_MATH_REG opcode, result_reg, result_reg, op1_reg
4283	} else {
4284		if (Z_MODE(res_addr) == IS_REG) {
4285			result_reg = Z_REG(res_addr);
4286		} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4287			result_reg = Z_REG(op1_addr);
4288		} else {
4289			result_reg = ZREG_FPR0;
4290		}
4291
4292		if (Z_MODE(op1_addr) == IS_REG) {
4293			op1_reg = Z_REG(op1_addr);
4294		} else {
4295			|	GET_ZVAL_DVAL result_reg, op1_addr, ZREG_TMP1
4296			op1_reg = result_reg;
4297		}
4298		if ((opcode == ZEND_ADD || opcode == ZEND_SUB)
4299		 && Z_MODE(op2_addr) == IS_CONST_ZVAL
4300		 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
4301			/* +/- 0 */
4302		} else {
4303			op2_reg = ZREG_FPTMP;
4304			|	DOUBLE_GET_ZVAL_LVAL op2_reg, op2_addr, ZREG_TMP1, ZREG_TMP2
4305			|	DOUBLE_MATH_REG opcode, result_reg, op1_reg, op2_reg
4306		}
4307	}
4308
4309	|	SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1
4310
4311	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4312		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4313			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4314				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
4315			}
4316		}
4317	}
4318
4319	return 1;
4320}
4321
4322static int zend_jit_math_double_double(dasm_State    **Dst,
4323                                       zend_uchar      opcode,
4324                                       zend_jit_addr   op1_addr,
4325                                       zend_jit_addr   op2_addr,
4326                                       zend_jit_addr   res_addr,
4327                                       uint32_t        res_use_info)
4328{
4329	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4330	zend_reg result_reg, op1_reg, op2_reg;
4331	zend_jit_addr val_addr;
4332
4333	if (Z_MODE(res_addr) == IS_REG) {
4334		result_reg = Z_REG(res_addr);
4335	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4336		result_reg = Z_REG(op1_addr);
4337	} else if (zend_is_commutative(opcode) && Z_MODE(op2_addr) == IS_REG && Z_LAST_USE(op2_addr)) {
4338		result_reg = Z_REG(op2_addr);
4339	} else {
4340		result_reg = ZREG_FPR0;
4341	}
4342
4343	if (Z_MODE(op1_addr) == IS_REG) {
4344		op1_reg = Z_REG(op1_addr);
4345		val_addr = op2_addr;
4346	} else if (Z_MODE(op2_addr) == IS_REG && zend_is_commutative(opcode)) {
4347		op1_reg = Z_REG(op2_addr);
4348		val_addr = op1_addr;
4349	} else {
4350		|	GET_ZVAL_DVAL result_reg, op1_addr, ZREG_TMP1
4351		op1_reg = result_reg;
4352		val_addr = op2_addr;
4353	}
4354
4355	if ((opcode == ZEND_MUL) &&
4356		Z_MODE(val_addr) == IS_CONST_ZVAL && Z_DVAL_P(Z_ZV(val_addr)) == 2.0) {
4357		|	DOUBLE_MATH_REG ZEND_ADD, result_reg, op1_reg, op1_reg
4358	} else {
4359		if (same_ops) {
4360			op2_reg = op1_reg;
4361		} else if (Z_MODE(val_addr) == IS_REG) {
4362			op2_reg = Z_REG(val_addr);
4363		} else {
4364			op2_reg = ZREG_FPTMP;
4365			|	GET_ZVAL_DVAL op2_reg, val_addr, ZREG_TMP1
4366		}
4367		|	DOUBLE_MATH_REG opcode, result_reg, op1_reg, op2_reg
4368	}
4369
4370	|	SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1
4371
4372	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4373		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4374			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) {
4375				|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
4376			}
4377		}
4378	}
4379	return 1;
4380}
4381
4382static int zend_jit_math_helper(dasm_State    **Dst,
4383                                const zend_op  *opline,
4384                                zend_uchar      opcode,
4385                                zend_uchar      op1_type,
4386                                znode_op        op1,
4387                                zend_jit_addr   op1_addr,
4388                                uint32_t        op1_info,
4389                                zend_uchar      op2_type,
4390                                znode_op        op2,
4391                                zend_jit_addr   op2_addr,
4392                                uint32_t        op2_info,
4393                                uint32_t        res_var,
4394                                zend_jit_addr   res_addr,
4395                                uint32_t        res_info,
4396                                uint32_t        res_use_info,
4397                                int             may_overflow,
4398                                int             may_throw)
4399/* Labels: 1,2,3,4,5,6 */
4400{
4401	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4402
4403	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
4404		if (op1_info & (MAY_BE_ANY-MAY_BE_LONG)) {
4405			if (op1_info & MAY_BE_DOUBLE) {
4406				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1
4407			} else {
4408				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
4409			}
4410		}
4411		if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_LONG))) {
4412			if (op2_info & MAY_BE_DOUBLE) {
4413				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >1, ZREG_TMP1
4414				|.cold_code
4415				|1:
4416				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4417					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1
4418				}
4419				if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4420					return 0;
4421				}
4422				|	b >5
4423				|.code
4424			} else {
4425				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
4426			}
4427		}
4428		if (!zend_jit_math_long_long(Dst, opline, opcode, op1_addr, op2_addr, res_addr, res_info, res_use_info, may_overflow)) {
4429			return 0;
4430		}
4431		if (op1_info & MAY_BE_DOUBLE) {
4432			|.cold_code
4433			|3:
4434			if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4435				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1
4436			}
4437			if (op2_info & MAY_BE_DOUBLE) {
4438				if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4439					if (!same_ops) {
4440						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >1, ZREG_TMP1
4441					} else {
4442						|	IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >6, ZREG_TMP1
4443					}
4444				}
4445				if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4446					return 0;
4447				}
4448				|	b >5
4449			}
4450			if (!same_ops) {
4451				|1:
4452				if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
4453					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
4454				}
4455				if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4456					return 0;
4457				}
4458				|	b >5
4459			}
4460			|.code
4461		}
4462	} else if ((op1_info & MAY_BE_DOUBLE) &&
4463	           !(op1_info & MAY_BE_LONG) &&
4464	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4465	           (res_info & MAY_BE_DOUBLE)) {
4466		if (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
4467			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1
4468		}
4469		if (op2_info & MAY_BE_DOUBLE) {
4470			if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4471				if (!same_ops && (op2_info & MAY_BE_LONG)) {
4472					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >1, ZREG_TMP1
4473				} else {
4474					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1
4475				}
4476			}
4477			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4478				return 0;
4479			}
4480		}
4481		if (!same_ops && (op2_info & MAY_BE_LONG)) {
4482			if (op2_info & MAY_BE_DOUBLE) {
4483				|.cold_code
4484			}
4485		    |1:
4486			if (op2_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
4487				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
4488			}
4489			if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4490				return 0;
4491			}
4492			if (op2_info & MAY_BE_DOUBLE) {
4493				|	b >5
4494				|.code
4495			}
4496		}
4497	} else if ((op2_info & MAY_BE_DOUBLE) &&
4498	           !(op2_info & MAY_BE_LONG) &&
4499	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4500	           (res_info & MAY_BE_DOUBLE)) {
4501		if (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) {
4502			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1
4503		}
4504		if (op1_info & MAY_BE_DOUBLE) {
4505			if (!same_ops && (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) {
4506				if (!same_ops && (op1_info & MAY_BE_LONG)) {
4507					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >1, ZREG_TMP1
4508				} else {
4509					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1
4510				}
4511			}
4512			if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4513				return 0;
4514			}
4515		}
4516		if (!same_ops && (op1_info & MAY_BE_LONG)) {
4517			if (op1_info & MAY_BE_DOUBLE) {
4518				|.cold_code
4519			}
4520			|1:
4521			if (op1_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
4522				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
4523			}
4524			if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) {
4525				return 0;
4526			}
4527			if (op1_info & MAY_BE_DOUBLE) {
4528				|	b >5
4529				|.code
4530			}
4531		}
4532	}
4533
4534	|5:
4535
4536	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
4537		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
4538		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4539		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4540		    (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
4541			|.cold_code
4542		}
4543		|6:
4544		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
4545			if (Z_MODE(res_addr) == IS_REG) {
4546				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4547				|	LOAD_ZVAL_ADDR FCARG1x, real_addr
4548			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
4549				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
4550			}
4551			if (Z_MODE(op1_addr) == IS_REG) {
4552				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
4553				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
4554					return 0;
4555				}
4556				op1_addr = real_addr;
4557			}
4558			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
4559		} else {
4560			if (Z_MODE(op1_addr) == IS_REG) {
4561				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
4562				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
4563					return 0;
4564				}
4565				op1_addr = real_addr;
4566			}
4567			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
4568			if (Z_MODE(res_addr) == IS_REG) {
4569				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4570				|	LOAD_ZVAL_ADDR FCARG1x, real_addr
4571			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
4572				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
4573			}
4574		}
4575		if (Z_MODE(op2_addr) == IS_REG) {
4576			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
4577			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
4578				return 0;
4579			}
4580			op2_addr = real_addr;
4581		}
4582		|	LOAD_ZVAL_ADDR CARG3, op2_addr
4583		|	SET_EX_OPLINE opline, REG0
4584		if (opcode == ZEND_ADD) {
4585			|	EXT_CALL add_function, REG0
4586		} else if (opcode == ZEND_SUB) {
4587			|	EXT_CALL sub_function, REG0
4588		} else if (opcode == ZEND_MUL) {
4589			|	EXT_CALL mul_function, REG0
4590		} else if (opcode == ZEND_DIV) {
4591			|	EXT_CALL div_function, REG0
4592		} else {
4593			ZEND_UNREACHABLE();
4594		}
4595		|	FREE_OP op1_type, op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
4596		|	FREE_OP op2_type, op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
4597		if (may_throw) {
4598			if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
4599				|	MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1
4600				|	cbnz TMP2, ->exception_handler_free_op2
4601			} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
4602				zend_jit_check_exception_undef_result(Dst, opline);
4603			} else {
4604				zend_jit_check_exception(Dst);
4605			}
4606		}
4607		if (Z_MODE(res_addr) == IS_REG) {
4608			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4609			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
4610				return 0;
4611			}
4612		}
4613		if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4614		    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4615		    (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
4616			|	b <5
4617			|.code
4618		}
4619	}
4620
4621	return 1;
4622}
4623
4624static 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)
4625{
4626	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
4627	ZEND_ASSERT((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
4628	    (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)));
4629
4630	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)) {
4631		return 0;
4632	}
4633	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
4634		return 0;
4635	}
4636	return 1;
4637}
4638
4639static 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)
4640{
4641	if (Z_MODE(op2_addr) != IS_MEM_ZVAL || Z_REG(op2_addr) != ZREG_FCARG1) {
4642		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1
4643		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
4644	} else if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
4645		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
4646		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1
4647	} else {
4648		|	GET_ZVAL_LVAL ZREG_REG0, op2_addr, TMP1
4649		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1
4650		|	mov FCARG2x, REG0
4651	}
4652	|	EXT_CALL zend_jit_add_arrays_helper, REG0
4653	|	SET_ZVAL_PTR res_addr, RETVALx, TMP1
4654	|	SET_ZVAL_TYPE_INFO res_addr, IS_ARRAY_EX, TMP1w, TMP2
4655	|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
4656	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
4657	return 1;
4658}
4659
4660static int zend_jit_long_math_helper(dasm_State    **Dst,
4661                                     const zend_op  *opline,
4662                                     zend_uchar      opcode,
4663                                     zend_uchar      op1_type,
4664                                     znode_op        op1,
4665                                     zend_jit_addr   op1_addr,
4666                                     uint32_t        op1_info,
4667                                     zend_ssa_range *op1_range,
4668                                     zend_uchar      op2_type,
4669                                     znode_op        op2,
4670                                     zend_jit_addr   op2_addr,
4671                                     uint32_t        op2_info,
4672                                     zend_ssa_range *op2_range,
4673                                     uint32_t        res_var,
4674                                     zend_jit_addr   res_addr,
4675                                     uint32_t        res_info,
4676                                     uint32_t        res_use_info,
4677                                     int             may_throw)
4678/* Labels: 6 */
4679{
4680	bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
4681	zend_reg result_reg;
4682
4683	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
4684		|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
4685	}
4686	if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
4687		|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1
4688	}
4689
4690	if (Z_MODE(res_addr) == IS_REG) {
4691		if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR)
4692		 && opline->op2_type != IS_CONST) {
4693			result_reg = ZREG_REG0;
4694		} else {
4695			result_reg = Z_REG(res_addr);
4696		}
4697	} else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) {
4698		result_reg = Z_REG(op1_addr);
4699	} else if (Z_REG(res_addr) != ZREG_REG0) {
4700		result_reg = ZREG_REG0;
4701	} else {
4702		/* ASSIGN_DIM_OP */
4703		result_reg = ZREG_FCARG1;
4704	}
4705
4706	if (opcode == ZEND_SL) {
4707		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
4708			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
4709
4710			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
4711				if (EXPECTED(op2_lval > 0)) {
4712					|	mov Rx(result_reg), xzr
4713				} else {
4714					zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
4715					zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
4716					|	SET_EX_OPLINE opline, REG0
4717					|	b ->negative_shift
4718				}
4719			} else if (Z_MODE(op1_addr) == IS_REG && op2_lval == 1) {
4720				|	add Rx(result_reg), Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr))
4721			} else {
4722				|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4723				|	lsl Rx(result_reg), Rx(result_reg), #op2_lval
4724			}
4725		} else {
4726			zend_reg op2_reg;
4727
4728			if (Z_MODE(op2_addr) == IS_REG) {
4729				op2_reg = Z_REG(op2_addr);
4730			} else {
4731				op2_reg = ZREG_TMP2;
4732				|	GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2
4733			}
4734			if (!op2_range ||
4735			     op2_range->min < 0 ||
4736			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
4737
4738				|	cmp Rx(op2_reg), #(SIZEOF_ZEND_LONG*8)
4739				|	bhs >1
4740				|.cold_code
4741				|1:
4742				|	mov Rx(result_reg), xzr
4743				|	cmp Rx(op2_reg), xzr
4744				|	bgt >1
4745				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
4746				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
4747				|	SET_EX_OPLINE opline, REG0
4748				|	b ->negative_shift
4749				|.code
4750			}
4751			|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4752			|	lsl Rx(result_reg), Rx(result_reg), Rx(op2_reg)
4753			|1:
4754		}
4755	} else if (opcode == ZEND_SR) {
4756		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4757		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
4758			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
4759
4760			if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
4761				if (EXPECTED(op2_lval > 0)) {
4762					|	asr Rx(result_reg), Rx(result_reg), #((SIZEOF_ZEND_LONG * 8) - 1)
4763				} else {
4764					zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
4765					zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
4766					|	SET_EX_OPLINE opline, REG0
4767					|	b ->negative_shift
4768				}
4769			} else {
4770				|	asr Rx(result_reg), Rx(result_reg), #op2_lval
4771			}
4772		} else {
4773			zend_reg op2_reg;
4774
4775			if (Z_MODE(op2_addr) == IS_REG) {
4776				op2_reg = Z_REG(op2_addr);
4777			} else {
4778				op2_reg = ZREG_TMP2;
4779				|	GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2
4780			}
4781			if (!op2_range ||
4782			     op2_range->min < 0 ||
4783			     op2_range->max >= SIZEOF_ZEND_LONG * 8) {
4784				|	cmp Rx(op2_reg), #(SIZEOF_ZEND_LONG*8)
4785				|	bhs >1
4786				|.cold_code
4787				|1:
4788				|	cmp Rx(op2_reg), xzr
4789				|	mov Rx(op2_reg), #((SIZEOF_ZEND_LONG * 8) - 1)
4790				|	bgt >1
4791				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
4792				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
4793				|	SET_EX_OPLINE opline, REG0
4794				|	b ->negative_shift
4795				|.code
4796			}
4797			|1:
4798			|	asr Rx(result_reg), Rx(result_reg), Rx(op2_reg)
4799		}
4800	} else if (opcode == ZEND_MOD) {
4801		if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
4802			zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr));
4803
4804			if (op2_lval == 0) {
4805					zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
4806					zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
4807				|	SET_EX_OPLINE opline, REG0
4808				|	b ->mod_by_zero
4809			} else if (op2_lval == -1) {
4810				|	mov Rx(result_reg), xzr
4811			} else if (zend_long_is_power_of_two(op2_lval) && op1_range && op1_range->min >= 0) {
4812				zval tmp;
4813				zend_jit_addr tmp_addr;
4814
4815				/* Optimisation for mod of power of 2 */
4816				ZVAL_LONG(&tmp, op2_lval - 1);
4817				tmp_addr = ZEND_ADDR_CONST_ZVAL(&tmp);
4818				|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4819				|	LONG_MATH ZEND_BW_AND, result_reg, tmp_addr, TMP1
4820			} else {
4821				|	GET_ZVAL_LVAL ZREG_TMP1, op1_addr, TMP1
4822				|	GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2
4823				|	sdiv Rx(result_reg), TMP1, TMP2
4824				|	msub Rx(result_reg), Rx(result_reg), TMP2, TMP1
4825			}
4826		} else {
4827			zend_reg op2_reg;
4828
4829			if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
4830				|	MEM_ACCESS_64_WITH_UOFFSET ldr, TMP2, Rx(Z_REG(op2_addr)), Z_OFFSET(op2_addr), TMP2
4831				op2_reg = ZREG_TMP2;
4832			} else {
4833				ZEND_ASSERT(Z_MODE(op2_addr) == IS_REG);
4834				op2_reg = Z_REG(op2_addr);
4835			}
4836
4837			if ((op2_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) || !op2_range || (op2_range->min <= 0 && op2_range->max >= 0)) {
4838				|	cbz Rx(op2_reg), >1
4839				|.cold_code
4840				|1:
4841				zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1);
4842				zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2);
4843				|	SET_EX_OPLINE opline, REG0
4844				|	b ->mod_by_zero
4845				|.code
4846			}
4847
4848			/* Prevent overflow error/crash if op1 == LONG_MIN and op2 == -1 */
4849			if (!op2_range || (op2_range->min <= -1 && op2_range->max >= -1)) {
4850				|	cmn Rx(op2_reg), #1
4851				|	beq >1
4852				|.cold_code
4853				|1:
4854				|	SET_ZVAL_LVAL_FROM_REG res_addr, xzr, TMP1
4855				if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4856					if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4857						if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
4858							|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
4859						}
4860					}
4861				}
4862				|	b >5
4863				|.code
4864			}
4865
4866			|	GET_ZVAL_LVAL ZREG_TMP1, op1_addr, TMP1
4867			|	sdiv Rx(result_reg), TMP1, Rx(op2_reg)
4868			|	msub Rx(result_reg), Rx(result_reg), Rx(op2_reg), TMP1
4869		}
4870	} else if (same_ops) {
4871		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4872		|	LONG_MATH_REG opcode, Rx(result_reg), Rx(result_reg), Rx(result_reg)
4873	} else {
4874		|	GET_ZVAL_LVAL result_reg, op1_addr, TMP1
4875		|	LONG_MATH opcode, result_reg, op2_addr, TMP1
4876	}
4877
4878	if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != result_reg) {
4879		|	SET_ZVAL_LVAL_FROM_REG res_addr, Rx(result_reg), TMP1
4880	}
4881	if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
4882		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) {
4883			if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) {
4884				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
4885			}
4886		}
4887	}
4888
4889	if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) ||
4890		(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
4891		if ((op1_info & MAY_BE_LONG) &&
4892		    (op2_info & MAY_BE_LONG)) {
4893			|.cold_code
4894		}
4895		|6:
4896		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
4897			if (Z_MODE(res_addr) == IS_REG) {
4898				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4899				|	LOAD_ZVAL_ADDR FCARG1x, real_addr
4900			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
4901				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
4902			}
4903			if (Z_MODE(op1_addr) == IS_REG) {
4904				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
4905				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
4906					return 0;
4907				}
4908				op1_addr = real_addr;
4909			}
4910			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
4911		} else {
4912			if (Z_MODE(op1_addr) == IS_REG) {
4913				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var);
4914				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
4915					return 0;
4916				}
4917				op1_addr = real_addr;
4918			}
4919			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
4920			if (Z_MODE(res_addr) == IS_REG) {
4921				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4922				|	LOAD_ZVAL_ADDR FCARG1x, real_addr
4923			} else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
4924				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
4925			}
4926		}
4927		if (Z_MODE(op2_addr) == IS_REG) {
4928			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var);
4929			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
4930				return 0;
4931			}
4932			op2_addr = real_addr;
4933		}
4934		|	LOAD_ZVAL_ADDR CARG3, op2_addr
4935		|	SET_EX_OPLINE opline, REG0
4936		if (opcode == ZEND_BW_OR) {
4937			|	EXT_CALL bitwise_or_function, REG0
4938		} else if (opcode == ZEND_BW_AND) {
4939			|	EXT_CALL bitwise_and_function, REG0
4940		} else if (opcode == ZEND_BW_XOR) {
4941			|	EXT_CALL bitwise_xor_function, REG0
4942		} else if (opcode == ZEND_SL) {
4943			|	EXT_CALL shift_left_function, REG0
4944		} else if (opcode == ZEND_SR) {
4945			|	EXT_CALL shift_right_function, REG0
4946		} else if (opcode == ZEND_MOD) {
4947			|	EXT_CALL mod_function, REG0
4948		} else {
4949			ZEND_UNREACHABLE();
4950		}
4951		if (op1_addr == res_addr && (op2_info & MAY_BE_RCN)) {
4952			/* compound assignment may decrement "op2" refcount */
4953			op2_info |= MAY_BE_RC1;
4954		}
4955		|	FREE_OP op1_type, op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
4956		|	FREE_OP op2_type, op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
4957		if (may_throw) {
4958			if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
4959				|	MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1
4960				|	cbnz TMP2, ->exception_handler_free_op2
4961			} else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
4962				zend_jit_check_exception_undef_result(Dst, opline);
4963			} else {
4964				zend_jit_check_exception(Dst);
4965			}
4966		}
4967		if (Z_MODE(res_addr) == IS_REG) {
4968			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var);
4969			if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) {
4970				return 0;
4971			}
4972		}
4973		if ((op1_info & MAY_BE_LONG) &&
4974		    (op2_info & MAY_BE_LONG)) {
4975			|	b >5
4976			|.code
4977		}
4978	}
4979	|5:
4980
4981	return 1;
4982}
4983
4984static 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)
4985{
4986	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
4987	ZEND_ASSERT((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG));
4988
4989	if (!zend_jit_long_math_helper(Dst, opline, opline->opcode,
4990			opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
4991			opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
4992			opline->result.var, res_addr, res_info, res_use_info, may_throw)) {
4993		return 0;
4994	}
4995	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
4996		return 0;
4997	}
4998	return 1;
4999}
5000
5001static int zend_jit_concat_helper(dasm_State    **Dst,
5002                                  const zend_op  *opline,
5003                                  zend_uchar      op1_type,
5004                                  znode_op        op1,
5005                                  zend_jit_addr   op1_addr,
5006                                  uint32_t        op1_info,
5007                                  zend_uchar      op2_type,
5008                                  znode_op        op2,
5009                                  zend_jit_addr   op2_addr,
5010                                  uint32_t        op2_info,
5011                                  zend_jit_addr   res_addr,
5012                                  int             may_throw)
5013{
5014	if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5015		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
5016			|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1
5017		}
5018		if (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) {
5019			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >6, ZREG_TMP1
5020		}
5021		if (Z_MODE(op1_addr) == IS_MEM_ZVAL && Z_REG(op1_addr) == Z_REG(res_addr) && Z_OFFSET(op1_addr) == Z_OFFSET(res_addr)) {
5022			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5023				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
5024			}
5025			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
5026			|	EXT_CALL zend_jit_fast_assign_concat_helper, REG0
5027			/* concatination with itself may reduce refcount */
5028			op2_info |= MAY_BE_RC1;
5029		} else {
5030			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5031				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
5032			}
5033			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
5034			|	LOAD_ZVAL_ADDR CARG3, op2_addr
5035			if (op1_type == IS_CV || op1_type == IS_CONST) {
5036				|	EXT_CALL zend_jit_fast_concat_helper, REG0
5037			} else {
5038				|	EXT_CALL zend_jit_fast_concat_tmp_helper, REG0
5039			}
5040		}
5041		/* concatination with empty string may increase refcount */
5042		op2_info |= MAY_BE_RCN;
5043		|	FREE_OP op2_type, op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
5044		|5:
5045	}
5046	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) ||
5047	    (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING))) {
5048		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5049			|.cold_code
5050			|6:
5051		}
5052		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) {
5053			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5054				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
5055			}
5056			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
5057		} else {
5058			|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
5059			if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
5060				|	LOAD_ZVAL_ADDR FCARG1x, res_addr
5061			}
5062		}
5063		|	LOAD_ZVAL_ADDR CARG3, op2_addr
5064		|	SET_EX_OPLINE opline, REG0
5065		|	EXT_CALL concat_function, REG0
5066		/* concatination with empty string may increase refcount */
5067		op1_info |= MAY_BE_RCN;
5068		op2_info |= MAY_BE_RCN;
5069		|	FREE_OP op1_type, op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
5070		|	FREE_OP op2_type, op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
5071		if (may_throw) {
5072			if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) {
5073				zend_jit_check_exception_undef_result(Dst, opline);
5074			} else {
5075				zend_jit_check_exception(Dst);
5076			}
5077		}
5078		if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) {
5079			|	b <5
5080			|.code
5081		}
5082	}
5083
5084	return 1;
5085}
5086
5087static 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)
5088{
5089	zend_jit_addr op1_addr, op2_addr;
5090
5091	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
5092	ZEND_ASSERT((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING));
5093
5094	op1_addr = OP1_ADDR();
5095	op2_addr = OP2_ADDR();
5096
5097	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);
5098}
5099
5100static 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)
5101/* Labels: 1,2,3,4,5 */
5102{
5103	zend_jit_addr op2_addr = OP2_ADDR();
5104	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
5105
5106	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
5107	 && type == BP_VAR_R
5108	 && !exit_addr) {
5109		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
5110		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
5111		if (!exit_addr) {
5112			return 0;
5113		}
5114	}
5115
5116	if (op2_info & MAY_BE_LONG) {
5117		bool op2_loaded = 0;
5118		bool packed_loaded = 0;
5119		bool bad_packed_key = 0;
5120
5121		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) {
5122			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_LONG))
5123			|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3, ZREG_TMP1
5124		}
5125		if (op1_info & MAY_BE_PACKED_GUARD) {
5126			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
5127			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
5128
5129			if (!exit_addr) {
5130				return 0;
5131			}
5132			if (op1_info & MAY_BE_ARRAY_PACKED) {
5133				|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
5134				|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
5135				|	beq &exit_addr
5136			} else {
5137				|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
5138				|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
5139				|	bne &exit_addr
5140			}
5141		}
5142		if (type == BP_VAR_W) {
5143			|	// hval = Z_LVAL_P(dim);
5144			|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
5145			op2_loaded = 1;
5146		}
5147		if (op1_info & MAY_BE_ARRAY_PACKED) {
5148			zend_long val = -1;
5149
5150			if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5151				val = Z_LVAL_P(Z_ZV(op2_addr));
5152				if (val >= 0 && val < HT_MAX_SIZE) {
5153					packed_loaded = 1;
5154				} else {
5155					bad_packed_key = 1;
5156				}
5157			} else {
5158				if (!op2_loaded) {
5159					|	// hval = Z_LVAL_P(dim);
5160					|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
5161					op2_loaded = 1;
5162				}
5163				packed_loaded = 1;
5164			}
5165
5166			if (dim_type == IS_UNDEF && type == BP_VAR_W) {
5167				/* don't generate "fast" code for packed array */
5168				packed_loaded = 0;
5169			}
5170
5171			if (packed_loaded) {
5172				|	// ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
5173				if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5174					|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)]
5175					|	TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w
5176					|	beq >4 // HASH_FIND
5177				}
5178				|	// if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed))
5179
5180				|	ldr REG0w, [FCARG1x, #offsetof(zend_array, nNumUsed)]
5181				if (val == 0) {
5182					|	cmp REG0, xzr
5183				} else if (val > 0 && !op2_loaded) {
5184					|	CMP_64_WITH_CONST REG0, val, TMP1
5185				} else {
5186					|	cmp REG0, FCARG2x
5187				}
5188
5189				if (type == BP_JIT_IS) {
5190					if (not_found_exit_addr) {
5191						|	bls &not_found_exit_addr
5192					} else {
5193						|	bls >9 // NOT_FOUND
5194					}
5195				} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5196					|	bls &exit_addr
5197				} else if (type == BP_VAR_IS && not_found_exit_addr) {
5198					|	bls &not_found_exit_addr
5199				} else if (type == BP_VAR_RW && not_found_exit_addr) {
5200					|	bls &not_found_exit_addr
5201				} else if (type == BP_VAR_IS && found_exit_addr) {
5202					|	bls >7 // NOT_FOUND
5203				} else {
5204					|	bls >2 // NOT_FOUND
5205				}
5206				|	// _ret = &_ht->arData[_h].val;
5207				if (val >= 0) {
5208					|	ldr REG0, [FCARG1x, #offsetof(zend_array, arData)]
5209					if (val != 0) {
5210						|	ADD_SUB_64_WITH_CONST add, REG0, REG0, (val * sizeof(Bucket)), TMP1
5211					}
5212				} else {
5213					|	ldr TMP1, [FCARG1x, #offsetof(zend_array, arData)]
5214					|	add REG0, TMP1, FCARG2x, lsl #5
5215				}
5216			}
5217		}
5218		switch (type) {
5219			case BP_JIT_IS:
5220				if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5221					if (packed_loaded) {
5222						|	b >5
5223					}
5224					|4:
5225					if (!op2_loaded) {
5226						|	// hval = Z_LVAL_P(dim);
5227						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
5228					}
5229					if (packed_loaded) {
5230						|	EXT_CALL _zend_hash_index_find, REG0
5231					} else {
5232						|	EXT_CALL zend_hash_index_find, REG0
5233					}
5234					|	mov REG0, RETVALx
5235					if (not_found_exit_addr) {
5236						|	cbz REG0, &not_found_exit_addr
5237					} else {
5238						|	cbz REG0, >9 // NOT_FOUND
5239					}
5240					if (op2_info & MAY_BE_STRING) {
5241						|	b >5
5242					}
5243				} else if (packed_loaded) {
5244					if (op2_info & MAY_BE_STRING) {
5245						|	b >5
5246					}
5247				} else if (not_found_exit_addr) {
5248					|	b &not_found_exit_addr
5249				} else {
5250					|	b >9 // NOT_FOUND
5251				}
5252				break;
5253			case BP_VAR_R:
5254			case BP_VAR_IS:
5255			case BP_VAR_UNSET:
5256				if (packed_loaded) {
5257					if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) {
5258						|	IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w
5259					} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5260						/* perform IS_UNDEF check only after result type guard (during deoptimization) */
5261						if (!found_exit_addr || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5262							|	IF_Z_TYPE REG0, IS_UNDEF, &exit_addr, TMP1w
5263						}
5264					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5265						|	IF_Z_TYPE REG0, IS_UNDEF, &not_found_exit_addr, TMP1w
5266					} else if (type == BP_VAR_IS && found_exit_addr) {
5267						|	IF_Z_TYPE REG0, IS_UNDEF, >7, TMP1w // NOT_FOUND
5268					} else {
5269						|	IF_Z_TYPE REG0, IS_UNDEF, >2, TMP1w // NOT_FOUND
5270					}
5271				}
5272				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (packed_loaded && (op1_info & MAY_BE_ARRAY_NUMERIC_HASH))) {
5273					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5274						|	b &exit_addr
5275					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5276						|	b &not_found_exit_addr
5277					} else if (type == BP_VAR_IS && found_exit_addr) {
5278						|	b >7 // NOT_FOUND
5279					} else {
5280						|	b >2 // NOT_FOUND
5281					}
5282				}
5283				if (!packed_loaded || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5284					|4:
5285					if (!op2_loaded) {
5286						|	// hval = Z_LVAL_P(dim);
5287						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
5288					}
5289					if (packed_loaded) {
5290						|	EXT_CALL _zend_hash_index_find, REG0
5291					} else {
5292						|	EXT_CALL zend_hash_index_find, REG0
5293					}
5294					|	mov REG0, RETVALx
5295					if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5296						|	cbz REG0, &exit_addr
5297					} else if (type == BP_VAR_IS && not_found_exit_addr) {
5298						|	cbz REG0, &not_found_exit_addr
5299					} else if (type == BP_VAR_IS && found_exit_addr) {
5300						|	cbz REG0, >7 // NOT_FOUND
5301					} else {
5302						|	cbz REG0, >2 // NOT_FOUND
5303					}
5304				}
5305				|.cold_code
5306				|2:
5307				switch (type) {
5308					case BP_VAR_R:
5309						if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
5310							|	// zend_error(E_WARNING,"Undefined array key " ZEND_LONG_FMT, hval);
5311							|	// retval = &EG(uninitialized_zval);
5312							|	UNDEFINED_OFFSET opline
5313							|	b >9
5314						}
5315						break;
5316					case BP_VAR_IS:
5317					case BP_VAR_UNSET:
5318						if (!not_found_exit_addr && !found_exit_addr) {
5319							|	// retval = &EG(uninitialized_zval);
5320							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
5321							|	b >9
5322						}
5323						break;
5324					default:
5325						ZEND_UNREACHABLE();
5326				}
5327				|.code
5328				break;
5329			case BP_VAR_RW:
5330				if (packed_loaded && !not_found_exit_addr) {
5331					|	IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w
5332				}
5333				if (!packed_loaded ||
5334						!not_found_exit_addr ||
5335						(op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) {
5336					if (packed_loaded && not_found_exit_addr) {
5337						|.cold_code
5338					}
5339					|2:
5340					|4:
5341					if (!op2_loaded) {
5342						|	// hval = Z_LVAL_P(dim);
5343						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
5344					}
5345					if (packed_loaded) {
5346						|	EXT_CALL zend_jit_hash_index_lookup_rw_no_packed, REG0
5347					} else {
5348						|	EXT_CALL zend_jit_hash_index_lookup_rw, REG0
5349					}
5350					|	mov REG0, RETVALx
5351					if (not_found_exit_addr) {
5352						if (packed_loaded) {
5353							|	cbnz REG0, >8
5354							|	b &not_found_exit_addr
5355							|.code
5356						} else {
5357							|	cbz REG0, &not_found_exit_addr
5358						}
5359					} else {
5360						|	cbz REG0, >9
5361					}
5362				}
5363				break;
5364			case BP_VAR_W:
5365				if (packed_loaded) {
5366					|	IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w
5367				}
5368				if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) || packed_loaded || bad_packed_key || dim_type == IS_UNDEF) {
5369					|2:
5370					|4:
5371					if (!op2_loaded) {
5372						|	// hval = Z_LVAL_P(dim);
5373						|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
5374					}
5375					|	EXT_CALL zend_hash_index_lookup, REG0
5376					|	mov REG0, RETVALx
5377				}
5378				break;
5379			default:
5380				ZEND_UNREACHABLE();
5381		}
5382
5383		if (type != BP_JIT_IS && (op2_info & MAY_BE_STRING)) {
5384			|	b >8
5385		}
5386	}
5387
5388	if (op2_info & MAY_BE_STRING) {
5389		|3:
5390		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
5391			|	// if (EXPECTED(Z_TYPE_P(dim) == IS_STRING))
5392			|	IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >3, ZREG_TMP1
5393		}
5394		|	// offset_key = Z_STR_P(dim);
5395		|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
5396		|	// retval = zend_hash_find(ht, offset_key);
5397		switch (type) {
5398			case BP_JIT_IS:
5399				if (opline->op2_type != IS_CONST) {
5400					|	ldrb TMP1w, [FCARG2x, #offsetof(zend_string, val)]
5401					|	cmp TMP1w, #((uint8_t) ('9'))
5402					|	ble >1
5403					|.cold_code
5404					|1:
5405					|	EXT_CALL zend_jit_symtable_find, REG0
5406					|	b >1
5407					|.code
5408					|	EXT_CALL zend_hash_find, REG0
5409					|1:
5410				} else {
5411					|	EXT_CALL zend_hash_find_known_hash, REG0
5412				}
5413				|	mov REG0, RETVALx
5414				if (not_found_exit_addr) {
5415					|	cbz REG0, &not_found_exit_addr
5416				} else {
5417					|	cbz REG0, >9 // NOT_FOUND
5418				}
5419				break;
5420			case BP_VAR_R:
5421			case BP_VAR_IS:
5422			case BP_VAR_UNSET:
5423				if (opline->op2_type != IS_CONST) {
5424					|	ldrb TMP1w, [FCARG2x, #offsetof(zend_string, val)]
5425					|	cmp TMP1w, #((uint8_t) ('9'))
5426					|	ble >1
5427					|.cold_code
5428					|1:
5429					|	EXT_CALL zend_jit_symtable_find, REG0
5430					|	b >1
5431					|.code
5432					|	EXT_CALL zend_hash_find, REG0
5433					|1:
5434				} else {
5435					|	EXT_CALL zend_hash_find_known_hash, REG0
5436				}
5437				|	mov REG0, RETVALx
5438				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5439					|	cbz REG0, &exit_addr
5440				} else if (type == BP_VAR_IS && not_found_exit_addr) {
5441					|	cbz REG0, &not_found_exit_addr
5442				} else if (type == BP_VAR_IS && found_exit_addr) {
5443					|	cbz REG0, >7
5444				} else {
5445					|	cbz REG0, >2 // NOT_FOUND
5446					|.cold_code
5447					|2:
5448					switch (type) {
5449						case BP_VAR_R:
5450							// zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
5451							|	UNDEFINED_INDEX opline
5452							|	b >9
5453							break;
5454						case BP_VAR_IS:
5455						case BP_VAR_UNSET:
5456							|	// retval = &EG(uninitialized_zval);
5457							|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
5458							|	b >9
5459							break;
5460						default:
5461							ZEND_UNREACHABLE();
5462					}
5463					|.code
5464				}
5465				break;
5466			case BP_VAR_RW:
5467				if (opline->op2_type != IS_CONST) {
5468					|	EXT_CALL zend_jit_symtable_lookup_rw, REG0
5469				} else {
5470					|	EXT_CALL zend_jit_hash_lookup_rw, REG0
5471				}
5472				|	mov REG0, RETVALx
5473				if (not_found_exit_addr) {
5474					|	cbz REG0, &not_found_exit_addr
5475				} else {
5476					|	cbz REG0, >9
5477				}
5478				break;
5479			case BP_VAR_W:
5480				if (opline->op2_type != IS_CONST) {
5481					|	EXT_CALL zend_jit_symtable_lookup_w, REG0
5482				} else {
5483					|	EXT_CALL zend_hash_lookup, REG0
5484				}
5485				|	mov REG0, RETVALx
5486				break;
5487			default:
5488				ZEND_UNREACHABLE();
5489		}
5490	}
5491
5492	if (type == BP_JIT_IS && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))) {
5493	    |5:
5494		if (op1_info & MAY_BE_ARRAY_OF_REF) {
5495			|	ZVAL_DEREF REG0, MAY_BE_REF, TMP1w
5496		}
5497		|	ldrb TMP1w, [REG0,#offsetof(zval, u1.v.type)]
5498		|	cmp TMP1w, #IS_NULL
5499		if (not_found_exit_addr) {
5500			|	ble &not_found_exit_addr
5501		} else if (found_exit_addr) {
5502			|	bgt &found_exit_addr
5503		} else {
5504			|	ble >9 // NOT FOUND
5505		}
5506	}
5507
5508	if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
5509		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
5510			|.cold_code
5511			|3:
5512		}
5513		if (type != BP_VAR_RW) {
5514			|	SET_EX_OPLINE opline, REG0
5515		}
5516		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
5517		switch (type) {
5518			case BP_VAR_R:
5519				|	LOAD_ZVAL_ADDR CARG3, res_addr
5520				|	EXT_CALL zend_jit_fetch_dim_r_helper, REG0
5521				|	mov REG0, RETVALx
5522				|	b >9
5523				break;
5524			case BP_JIT_IS:
5525				|	EXT_CALL zend_jit_fetch_dim_isset_helper, REG0
5526				|	mov REG0, RETVALx
5527				if (not_found_exit_addr) {
5528					|	cbz REG0, &not_found_exit_addr
5529					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
5530						|	b >8
5531					}
5532				} else if (found_exit_addr) {
5533					|	cbnz REG0, &found_exit_addr
5534					if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
5535						|	b >9
5536					}
5537				} else {
5538					|	cbnz REG0, >8
5539					|	b >9
5540				}
5541				break;
5542			case BP_VAR_IS:
5543			case BP_VAR_UNSET:
5544				|	LOAD_ZVAL_ADDR CARG3, res_addr
5545				|	EXT_CALL zend_jit_fetch_dim_is_helper, REG0
5546				|	mov REG0, RETVALx
5547				|	b >9
5548				break;
5549			case BP_VAR_RW:
5550				|	EXT_CALL zend_jit_fetch_dim_rw_helper, REG0
5551				|	mov REG0, RETVALx
5552				|	cbnz REG0, >8
5553				|	b >9
5554				break;
5555			case BP_VAR_W:
5556				|	EXT_CALL zend_jit_fetch_dim_w_helper, REG0
5557				|	mov REG0, RETVALx
5558				|	cbnz REG0, >8
5559				|	b >9
5560				break;
5561			default:
5562				ZEND_UNREACHABLE();
5563		}
5564		if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) {
5565			|.code
5566		}
5567	}
5568
5569	return 1;
5570}
5571
5572static int zend_jit_simple_assign(dasm_State    **Dst,
5573                                  const zend_op  *opline,
5574                                  zend_jit_addr   var_addr,
5575                                  uint32_t        var_info,
5576                                  uint32_t        var_def_info,
5577                                  zend_uchar      val_type,
5578                                  zend_jit_addr   val_addr,
5579                                  uint32_t        val_info,
5580                                  zend_jit_addr   res_addr,
5581                                  int             in_cold,
5582                                  int             save_r1,
5583                                  bool            check_exception)
5584/* Labels: 1,2,3 */
5585{
5586	zend_reg tmp_reg;
5587
5588	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_addr) != ZREG_REG0) {
5589		tmp_reg = ZREG_REG0;
5590	} else {
5591		/* ASSIGN_DIM */
5592		tmp_reg = ZREG_FCARG1;
5593	}
5594
5595	if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
5596		zval *zv = Z_ZV(val_addr);
5597
5598		if (!res_addr) {
5599			|	ZVAL_COPY_CONST var_addr, var_info, var_def_info, zv, tmp_reg, ZREG_TMP1, ZREG_FPR0
5600		} else {
5601			|	ZVAL_COPY_CONST_2 var_addr, res_addr, var_info, var_def_info, zv, tmp_reg, ZREG_TMP1, ZREG_FPR0
5602		}
5603		if (Z_REFCOUNTED_P(zv)) {
5604			if (!res_addr) {
5605				|	ADDREF_CONST zv, TMP1, TMP2
5606			} else {
5607				|	ADDREF_CONST_2 zv, TMP1, TMP2
5608			}
5609		}
5610	} else {
5611		if (val_info & MAY_BE_UNDEF) {
5612			if (in_cold) {
5613				|	IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >2, ZREG_TMP1
5614			} else {
5615				|	IF_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1
5616				|.cold_code
5617				|1:
5618			}
5619			|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
5620			if (save_r1) {
5621				|	str FCARG1x, T1	// save
5622			}
5623			|	SET_ZVAL_TYPE_INFO var_addr, IS_NULL, TMP1w, TMP2
5624			if (res_addr) {
5625				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
5626			}
5627			if (opline) {
5628				|	SET_EX_OPLINE opline, Rx(tmp_reg)
5629			}
5630			ZEND_ASSERT(Z_MODE(val_addr) == IS_MEM_ZVAL && Z_REG(val_addr) == ZREG_FP);
5631			|	LOAD_32BIT_VAL FCARG1w, Z_OFFSET(val_addr)
5632			|	EXT_CALL zend_jit_undefined_op_helper, REG0
5633			if (check_exception) {
5634				|	cbz RETVALx, ->exception_handler_undef
5635			}
5636			if (save_r1) {
5637				|	ldr FCARG1x, T1	// restore
5638			}
5639			|	b >3
5640			if (in_cold) {
5641				|2:
5642			} else {
5643				|.code
5644			}
5645		}
5646		if (val_info & MAY_BE_REF) {
5647			if (val_type == IS_CV) {
5648				ZEND_ASSERT(Z_REG(var_addr) != ZREG_REG2);
5649				if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_REG2 || Z_OFFSET(val_addr) != 0) {
5650					|	LOAD_ZVAL_ADDR REG2, val_addr
5651				}
5652				|	ZVAL_DEREF REG2, val_info, TMP1w
5653				val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, 0);
5654			} else {
5655				zend_jit_addr ref_addr;
5656
5657				if (in_cold) {
5658					|	IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, >1, ZREG_TMP1
5659				} else {
5660					|	IF_ZVAL_TYPE val_addr, IS_REFERENCE, >1, ZREG_TMP1
5661					|.cold_code
5662					|1:
5663				}
5664				if (Z_REG(val_addr) == ZREG_REG2) {
5665					|	str REG2, T1 // save
5666				}
5667				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
5668				|	GET_ZVAL_PTR REG2, val_addr, TMP1
5669				|	GC_DELREF REG2, TMP1w
5670				|	// ZVAL_COPY_VALUE(return_value, &ref->val);
5671				ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, offsetof(zend_reference, val));
5672				if (!res_addr) {
5673					|	ZVAL_COPY_VALUE var_addr, var_info, ref_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
5674				} else {
5675					|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, ref_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
5676				}
5677				|	beq >2 // GC_DELREF() reached zero
5678				|	IF_NOT_REFCOUNTED REG2w, >3, TMP1w
5679				if (!res_addr) {
5680					|	GC_ADDREF Rx(tmp_reg), TMP1w
5681				} else {
5682					|	GC_ADDREF_2 Rx(tmp_reg), TMP1w
5683				}
5684				|	b >3
5685				|2:
5686				if (res_addr) {
5687					|	IF_NOT_REFCOUNTED REG2w, >2, TMP1w
5688					|	GC_ADDREF Rx(tmp_reg), TMP1w
5689					|2:
5690				}
5691				if (Z_REG(val_addr) == ZREG_REG2) {
5692					|	ldr REG2, T1 // restore
5693				}
5694				if (save_r1) {
5695					|	str FCARG1x, T1 // save
5696				}
5697				|	GET_ZVAL_PTR FCARG1x, val_addr, TMP1
5698				|	EFREE_REFERENCE
5699				if (save_r1) {
5700					|	ldr FCARG1x, T1 // restore
5701				}
5702				|	b >3
5703				if (in_cold) {
5704					|1:
5705				} else {
5706					|.code
5707				}
5708			}
5709		}
5710
5711		if (!res_addr) {
5712			|	ZVAL_COPY_VALUE var_addr, var_info, val_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
5713		} else {
5714			|	ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, val_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
5715		}
5716
5717		if (val_type == IS_CV) {
5718			if (!res_addr) {
5719				|	TRY_ADDREF val_info, REG2w, Rx(tmp_reg), TMP1w
5720			} else {
5721				|	TRY_ADDREF_2 val_info, REG2w, Rx(tmp_reg), TMP1w
5722			}
5723		} else {
5724			if (res_addr) {
5725				|	TRY_ADDREF val_info, REG2w, Rx(tmp_reg), TMP1w
5726			}
5727		}
5728		|3:
5729	}
5730	return 1;
5731}
5732
5733static int zend_jit_assign_to_typed_ref(dasm_State         **Dst,
5734                                       const zend_op        *opline,
5735                                       zend_uchar            val_type,
5736                                       zend_jit_addr         val_addr,
5737                                       zend_jit_addr         res_addr,
5738                                       bool                  check_exception)
5739{
5740	|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
5741	|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
5742	|	cbnz TMP1, >2
5743	|.cold_code
5744	|2:
5745	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
5746		|	LOAD_ZVAL_ADDR FCARG2x, val_addr
5747	}
5748	if (opline) {
5749		|	SET_EX_OPLINE opline, REG0
5750	}
5751	if (val_type == IS_CONST) {
5752		|	EXT_CALL zend_jit_assign_const_to_typed_ref, REG0
5753	} else if (val_type == IS_TMP_VAR) {
5754		|	EXT_CALL zend_jit_assign_tmp_to_typed_ref, REG0
5755	} else if (val_type == IS_VAR) {
5756		|	EXT_CALL zend_jit_assign_var_to_typed_ref, REG0
5757	} else if (val_type == IS_CV) {
5758		|	EXT_CALL zend_jit_assign_cv_to_typed_ref, REG0
5759	} else {
5760		ZEND_UNREACHABLE();
5761	}
5762	if (res_addr) {
5763		zend_jit_addr ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_X0, 0); // RETVAL
5764
5765		|	ZVAL_COPY_VALUE res_addr, -1, ret_addr, -1, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
5766		|	TRY_ADDREF -1, REG1w, REG2, TMP1w
5767	}
5768	if (check_exception) {
5769		|	// if (UNEXPECTED(EG(exception) != NULL)) {
5770		|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
5771		|	cbz REG0, >8  // END OF zend_jit_assign_to_variable()
5772		|	b ->exception_handler
5773	} else {
5774		|	b >8
5775	}
5776	|.code
5777
5778	return 1;
5779}
5780
5781static int zend_jit_assign_to_variable_call(dasm_State    **Dst,
5782                                            const zend_op  *opline,
5783                                            zend_jit_addr   __var_use_addr,
5784                                            zend_jit_addr   var_addr,
5785                                            uint32_t        __var_info,
5786                                            uint32_t        __var_def_info,
5787                                            zend_uchar      val_type,
5788                                            zend_jit_addr   val_addr,
5789                                            uint32_t        val_info,
5790                                            zend_jit_addr   __res_addr,
5791                                            bool            __check_exception)
5792{
5793	if (val_info & MAY_BE_UNDEF) {
5794		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
5795			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
5796			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
5797
5798			if (!exit_addr) {
5799				return 0;
5800			}
5801
5802			|	IF_ZVAL_TYPE val_addr, IS_UNDEF, &exit_addr, ZREG_TMP1
5803		} else {
5804			|	IF_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1
5805			|.cold_code
5806			|1:
5807			ZEND_ASSERT(Z_REG(val_addr) == ZREG_FP);
5808			if (Z_REG(var_addr) != ZREG_FP) {
5809				|	str Rx(Z_REG(var_addr)), T1 // save
5810			}
5811			|	SET_EX_OPLINE opline, REG0
5812			|	LOAD_32BIT_VAL FCARG1w, Z_OFFSET(val_addr)
5813			|	EXT_CALL zend_jit_undefined_op_helper, REG0
5814			if (Z_REG(var_addr) != ZREG_FP) {
5815				|	ldr Rx(Z_REG(var_addr)), T1 // restore
5816			}
5817			if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
5818				|	LOAD_ZVAL_ADDR FCARG1x, var_addr
5819			}
5820			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
5821			|	bl ->assign_const
5822			|	b >9
5823			|.code
5824			|1:
5825		}
5826	}
5827	if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
5828		|	LOAD_ZVAL_ADDR FCARG1x, var_addr
5829	}
5830	if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
5831		|	LOAD_ZVAL_ADDR FCARG2x, val_addr
5832	}
5833	if (opline) {
5834		|	SET_EX_OPLINE opline, REG0
5835	}
5836	if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
5837		|	bl ->assign_tmp
5838	} else if (val_type == IS_CONST) {
5839		|	bl ->assign_const
5840	} else if (val_type == IS_TMP_VAR) {
5841		|	bl ->assign_tmp
5842	} else if (val_type == IS_VAR) {
5843		if (!(val_info & MAY_BE_REF)) {
5844			|	bl ->assign_tmp
5845		} else {
5846			|	bl ->assign_var
5847		}
5848	} else if (val_type == IS_CV) {
5849		if (!(val_info & MAY_BE_REF)) {
5850			|	bl ->assign_cv_noref
5851		} else {
5852			|	bl ->assign_cv
5853		}
5854		if ((val_info & MAY_BE_UNDEF) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
5855			|9:
5856		}
5857	} else {
5858		ZEND_UNREACHABLE();
5859	}
5860
5861	return 1;
5862}
5863
5864static int zend_jit_assign_to_variable(dasm_State    **Dst,
5865                                       const zend_op  *opline,
5866                                       zend_jit_addr   var_use_addr,
5867                                       zend_jit_addr   var_addr,
5868                                       uint32_t        var_info,
5869                                       uint32_t        var_def_info,
5870                                       zend_uchar      val_type,
5871                                       zend_jit_addr   val_addr,
5872                                       uint32_t        val_info,
5873                                       zend_jit_addr   res_addr,
5874                                       bool            check_exception)
5875/* Labels: 1,2,3,4,5,8 */
5876{
5877	int done = 0;
5878	zend_reg ref_reg, tmp_reg;
5879
5880	if (Z_MODE(var_addr) == IS_REG || Z_REG(var_use_addr) != ZREG_REG0) {
5881		ref_reg = ZREG_FCARG1;
5882		tmp_reg = ZREG_REG0;
5883	} else {
5884		/* ASSIGN_DIM */
5885		ref_reg = ZREG_REG0;
5886		tmp_reg = ZREG_FCARG1;
5887	}
5888
5889	if (var_info & MAY_BE_REF) {
5890		if (Z_MODE(var_use_addr) != IS_MEM_ZVAL || Z_REG(var_use_addr) != ref_reg || Z_OFFSET(var_use_addr) != 0) {
5891			|	LOAD_ZVAL_ADDR Rx(ref_reg), var_use_addr
5892			var_addr = var_use_addr = ZEND_ADDR_MEM_ZVAL(ref_reg, 0);
5893		}
5894		|	// if (Z_ISREF_P(variable_ptr)) {
5895		|	IF_NOT_Z_TYPE Rx(ref_reg), IS_REFERENCE, >3, TMP1w
5896		|	// if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
5897		|	GET_Z_PTR FCARG1x, Rx(ref_reg)
5898		if (!zend_jit_assign_to_typed_ref(Dst, opline, val_type, val_addr, res_addr, check_exception)) {
5899			return 0;
5900		}
5901		|	add Rx(ref_reg), FCARG1x, #offsetof(zend_reference, val)
5902		|3:
5903	}
5904	if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
5905		if (RC_MAY_BE_1(var_info)) {
5906			int in_cold = 0;
5907
5908			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
5909				|	IF_ZVAL_REFCOUNTED var_use_addr, >1, ZREG_TMP1, ZREG_TMP2
5910				|.cold_code
5911				|1:
5912				in_cold = 1;
5913			}
5914			if (Z_REG(var_use_addr) == ZREG_FCARG1 || Z_REG(var_use_addr) == ZREG_REG0) {
5915				bool keep_gc = 0;
5916
5917				|	GET_ZVAL_PTR Rx(tmp_reg), var_use_addr, TMP1
5918#if 0
5919				// TODO: This optiization doesn't work on ARM
5920				if (tmp_reg == ZREG_FCARG1) {
5921					if (Z_MODE(val_addr) == IS_REG) {
5922						keep_gc = 1;
5923					} else if ((val_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) == 0) {
5924						keep_gc = 1;
5925					} else if (Z_MODE(val_addr) == IS_CONST_ZVAL) {
5926						zval *zv = Z_ZV(val_addr);
5927						if (Z_TYPE_P(zv) == IS_DOUBLE) {
5928							if (Z_DVAL_P(zv) == 0) {
5929								keep_gc = 1;
5930							}
5931						} else if (IS_SIGNED_32BIT(Z_LVAL_P(zv))) {
5932							keep_gc = 1;
5933						}
5934					} else if (Z_MODE(val_addr) == IS_MEM_ZVAL) {
5935						if ((val_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) {
5936							keep_gc = 1;
5937						}
5938					}
5939				}
5940#endif
5941				if (!keep_gc) {
5942					|	str Rx(tmp_reg), T1 // save
5943				}
5944				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)) {
5945					return 0;
5946				}
5947				if (!keep_gc) {
5948					|	ldr FCARG1x, T1     // restore
5949				}
5950			} else {
5951				|	GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1
5952				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)) {
5953					return 0;
5954				}
5955			}
5956			|	GC_DELREF FCARG1x, TMP1w
5957			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
5958				|	bne >4
5959			} else {
5960				|	bne >8
5961			}
5962			|	ZVAL_DTOR_FUNC var_info, opline, TMP1
5963			if (in_cold || (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0)) {
5964				if (check_exception && !(val_info & MAY_BE_UNDEF)) {
5965					|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
5966					|	cbz REG0, >8
5967					|	b ->exception_handler
5968				} else {
5969					|	b >8
5970				}
5971			}
5972			if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) {
5973				|4:
5974				|	IF_GC_MAY_NOT_LEAK FCARG1x, >8, TMP1w, TMP2w
5975				|	EXT_CALL gc_possible_root, REG0
5976				if (in_cold) {
5977					|	b >8
5978				}
5979			}
5980			if (check_exception && (val_info & MAY_BE_UNDEF)) {
5981				|8:
5982				|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
5983				|	cbz REG0, >8
5984				|	b ->exception_handler
5985			}
5986			if (in_cold) {
5987				|.code
5988			} else {
5989				done = 1;
5990			}
5991		} else /* if (RC_MAY_BE_N(var_info)) */ {
5992			if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
5993				|	IF_NOT_ZVAL_REFCOUNTED var_use_addr, >5, ZREG_TMP1, ZREG_TMP2
5994			}
5995			if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
5996				if (Z_REG(var_use_addr) != ZREG_FP) {
5997					|	str Rx(Z_REG(var_use_addr)), T1 // save
5998				}
5999				|	GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1
6000				|	GC_DELREF FCARG1x, TMP1w
6001				|	IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w
6002				|	EXT_CALL gc_possible_root, TMP1
6003				if (Z_REG(var_use_addr) != ZREG_FP) {
6004					|	ldr Rx(Z_REG(var_use_addr)), T1 // restore
6005				}
6006			} else {
6007				|	GET_ZVAL_PTR Rx(tmp_reg), var_use_addr, TMP1
6008				|	GC_DELREF Rx(tmp_reg), TMP1w
6009			}
6010			|5:
6011	    }
6012	}
6013
6014	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)) {
6015		return 0;
6016	}
6017
6018	|8:
6019
6020	return 1;
6021}
6022
6023static 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)
6024{
6025	zend_jit_addr op2_addr, op3_addr, res_addr;
6026
6027	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
6028	op3_addr = OP1_DATA_ADDR();
6029	if (opline->result_type == IS_UNUSED) {
6030		res_addr = 0;
6031	} else {
6032		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
6033	}
6034
6035	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (val_info & MAY_BE_UNDEF)) {
6036		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
6037		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6038
6039		if (!exit_addr) {
6040			return 0;
6041		}
6042
6043		|	IF_ZVAL_TYPE op3_addr, IS_UNDEF, &exit_addr, ZREG_TMP1
6044
6045		val_info &= ~MAY_BE_UNDEF;
6046	}
6047
6048	if (op1_info & MAY_BE_REF) {
6049		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
6050		|	IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w
6051		|	GET_Z_PTR FCARG2x, FCARG1x
6052		|	ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))]
6053		|	cmp TMP1w, #IS_ARRAY
6054		|	bne >2
6055		|	add FCARG1x, FCARG2x, #offsetof(zend_reference, val)
6056		|	b >3
6057		|.cold_code
6058		|2:
6059		|	SET_EX_OPLINE opline, REG0
6060		|	EXT_CALL zend_jit_prepare_assign_dim_ref, REG0
6061		|	mov FCARG1x, RETVALx
6062		|	cbnz FCARG1x, >1
6063		|	b ->exception_handler_undef
6064		|.code
6065		|1:
6066		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
6067	}
6068
6069	if (op1_info & MAY_BE_ARRAY) {
6070		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
6071			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
6072		}
6073		|3:
6074		|	SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2
6075	} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
6076		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6077			|	CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1
6078			|	bgt >7
6079		}
6080		|	// ZVAL_ARR(container, zend_new_array(8));
6081		if (Z_REG(op1_addr) != ZREG_FP) {
6082			|	str Rx(Z_REG(op1_addr)), T1 // save
6083		}
6084		|	EXT_CALL _zend_new_array_0, REG0
6085		|	mov REG0, RETVALx
6086		if (Z_REG(op1_addr) != ZREG_FP) {
6087			|	ldr Rx(Z_REG(op1_addr)), T1 // restore
6088		}
6089		|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
6090		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
6091		|	mov FCARG1x, REG0
6092	}
6093
6094	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6095		|6:
6096		if (opline->op2_type == IS_UNUSED) {
6097			uint32_t var_info = MAY_BE_NULL;
6098			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
6099
6100			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
6101			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
6102			|	EXT_CALL zend_hash_next_index_insert, REG0
6103			|	// if (UNEXPECTED(!var_ptr)) {
6104			|	mov REG0, RETVALx
6105			|	cbz REG0, >1
6106			|.cold_code
6107			|1:
6108			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
6109			|	CANNOT_ADD_ELEMENT opline
6110			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
6111			|	b >9
6112			|.code
6113
6114			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)) {
6115				return 0;
6116			}
6117		} else {
6118			uint32_t var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
6119			zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
6120
6121			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_W, op1_info, op2_info, dim_type, NULL, NULL, NULL)) {
6122				return 0;
6123			}
6124
6125			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
6126				var_info |= MAY_BE_REF;
6127			}
6128			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6129				var_info |= MAY_BE_RC1;
6130			}
6131
6132			|8:
6133			|	// value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE);
6134			if (opline->op1_type == IS_VAR) {
6135				ZEND_ASSERT(opline->result_type == IS_UNUSED);
6136				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)) {
6137					return 0;
6138				}
6139			} else {
6140				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)) {
6141					return 0;
6142				}
6143			}
6144		}
6145	}
6146
6147	if (((op1_info & MAY_BE_ARRAY) &&
6148	     (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) ||
6149	    (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY)))) {
6150		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6151			|.cold_code
6152			|7:
6153		}
6154
6155		if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) &&
6156		    (op1_info & MAY_BE_ARRAY)) {
6157			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6158				|	CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1
6159				|	bgt >2
6160			}
6161			|	// ZVAL_ARR(container, zend_new_array(8));
6162			if (Z_REG(op1_addr) != ZREG_FP) {
6163				|	str Rx(Z_REG(op1_addr)), T1 // save
6164			}
6165			|	EXT_CALL _zend_new_array_0, REG0
6166			|	mov REG0, RETVALx
6167			if (Z_REG(op1_addr) != ZREG_FP) {
6168				|	ldr Rx(Z_REG(op1_addr)), T1 // restore
6169			}
6170			|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
6171			|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
6172			|	mov FCARG1x, REG0
6173			|	// ZEND_VM_C_GOTO(assign_dim_op_new_array);
6174			|	b <6
6175			|2:
6176		}
6177
6178		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6179			|	SET_EX_OPLINE opline, REG0
6180		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
6181				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
6182			}
6183		    if (opline->op2_type == IS_UNUSED) {
6184				|	mov FCARG2x, xzr
6185			} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
6186				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
6187				|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
6188			} else {
6189				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
6190			}
6191			if (opline->result_type == IS_UNUSED) {
6192				|	mov CARG4, xzr
6193			} else {
6194				|	LOAD_ZVAL_ADDR CARG4, res_addr
6195			}
6196			|	LOAD_ZVAL_ADDR CARG3, op3_addr
6197			|	EXT_CALL zend_jit_assign_dim_helper, REG0
6198
6199#ifdef ZEND_JIT_USE_RC_INFERENCE
6200			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR)) && (val_info & MAY_BE_RC1)) {
6201				/* ASSIGN_DIM may increase refcount of the value */
6202				val_info |= MAY_BE_RCN;
6203			}
6204#endif
6205
6206			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
6207		}
6208
6209		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6210			if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6211				|	b >9 // END
6212			}
6213			|.code
6214		}
6215	}
6216
6217#ifdef ZEND_JIT_USE_RC_INFERENCE
6218	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))) {
6219		/* ASSIGN_DIM may increase refcount of the key */
6220		op2_info |= MAY_BE_RCN;
6221	}
6222#endif
6223
6224	|9:
6225	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
6226
6227	if (may_throw) {
6228		zend_jit_check_exception(Dst);
6229	}
6230
6231	return 1;
6232}
6233
6234static 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)
6235{
6236	zend_jit_addr op2_addr, op3_addr, var_addr;
6237	const void *not_found_exit_addr = NULL;
6238	uint32_t var_info = MAY_BE_NULL;
6239
6240	ZEND_ASSERT(opline->result_type == IS_UNUSED);
6241
6242	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
6243	op3_addr = OP1_DATA_ADDR();
6244
6245	|	SET_EX_OPLINE opline, REG0
6246	if (op1_info & MAY_BE_REF) {
6247		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
6248		|	IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w
6249		|	GET_Z_PTR FCARG2x, FCARG1x
6250		|	ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))]
6251		|	cmp TMP1w, #IS_ARRAY
6252		|	bne >2
6253		|	add FCARG1x, FCARG2x, #offsetof(zend_reference, val)
6254		|	b >3
6255		|.cold_code
6256		|2:
6257		|	EXT_CALL zend_jit_prepare_assign_dim_ref, REG0
6258		|	mov FCARG1x, RETVALx
6259		|	cbnz RETVALx, >1
6260		|	b ->exception_handler_undef
6261		|.code
6262		|1:
6263		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
6264	}
6265
6266	if (op1_info & MAY_BE_ARRAY) {
6267		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
6268			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
6269		}
6270		|3:
6271		|	SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2
6272	}
6273	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
6274		if (op1_info & MAY_BE_ARRAY) {
6275			|.cold_code
6276			|7:
6277		}
6278		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6279			|	CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1
6280			|	bgt >7
6281		}
6282		if (Z_REG(op1_addr) != ZREG_FP) {
6283			|	str Rx(Z_REG(op1_addr)), T1 // save
6284		}
6285		if (op1_info & MAY_BE_UNDEF) {
6286			if (op1_info & MAY_BE_NULL) {
6287				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
6288			}
6289			|	LOAD_32BIT_VAL FCARG1x, opline->op1.var
6290			|	EXT_CALL zend_jit_undefined_op_helper, REG0
6291			|1:
6292		}
6293		|	// ZVAL_ARR(container, zend_new_array(8));
6294		|	EXT_CALL _zend_new_array_0, REG0
6295		|	mov REG0, RETVALx
6296		if (Z_REG(op1_addr) != ZREG_FP) {
6297			|	ldr Rx(Z_REG(op1_addr)), T1 // restore
6298		}
6299		|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
6300		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
6301		|	mov FCARG1x, REG0
6302		if (op1_info & MAY_BE_ARRAY) {
6303			|	b >1
6304			|.code
6305			|1:
6306		}
6307	}
6308
6309	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6310		uint32_t var_def_info = zend_array_element_type(op1_def_info, opline->op1_type, 1, 0);
6311
6312		|6:
6313		if (opline->op2_type == IS_UNUSED) {
6314			var_info = MAY_BE_NULL;
6315
6316			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
6317			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
6318			|	EXT_CALL zend_hash_next_index_insert, REG0
6319			|	mov REG0, RETVALx
6320			|	// if (UNEXPECTED(!var_ptr)) {
6321			|	cbz REG0, >1
6322			|.cold_code
6323			|1:
6324			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
6325			|	CANNOT_ADD_ELEMENT opline
6326			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
6327			|	b >9
6328			|.code
6329		} else {
6330			var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
6331			if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) {
6332				var_info |= MAY_BE_REF;
6333			}
6334			if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
6335				var_info |= MAY_BE_RC1;
6336			}
6337
6338			if (dim_type != IS_UNKNOWN
6339			 && dim_type != IS_UNDEF
6340			 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY
6341			 && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))
6342			 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
6343				int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
6344				not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
6345				if (!not_found_exit_addr) {
6346					return 0;
6347				}
6348			}
6349
6350			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, dim_type, NULL, not_found_exit_addr, NULL)) {
6351				return 0;
6352			}
6353
6354			|8:
6355			if (not_found_exit_addr && dim_type != IS_REFERENCE) {
6356				|	IF_NOT_Z_TYPE, REG0, dim_type, &not_found_exit_addr, TMP1w
6357				var_info = (1 << dim_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
6358			}
6359			if (var_info & MAY_BE_REF) {
6360				binary_op_type binary_op = get_binary_op(opline->extended_value);
6361				|	IF_NOT_Z_TYPE, REG0, IS_REFERENCE, >1, TMP1w
6362				|	GET_Z_PTR FCARG1x, REG0
6363				|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
6364				|	cbnz TMP1, >2
6365				|	add REG0, FCARG1x, #offsetof(zend_reference, val)
6366				|.cold_code
6367				|2:
6368				|	LOAD_ZVAL_ADDR FCARG2x, op3_addr
6369				|	LOAD_ADDR CARG3, binary_op
6370				if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
6371				 && (op1_data_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6372					|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0
6373				} else {
6374					|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
6375				}
6376				|	b >9
6377				|.code
6378				|1:
6379			}
6380		}
6381
6382		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
6383		switch (opline->extended_value) {
6384			case ZEND_ADD:
6385			case ZEND_SUB:
6386			case ZEND_MUL:
6387			case ZEND_DIV:
6388				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,
6389						1 /* may overflow */, may_throw)) {
6390					return 0;
6391				}
6392				break;
6393			case ZEND_BW_OR:
6394			case ZEND_BW_AND:
6395			case ZEND_BW_XOR:
6396			case ZEND_SL:
6397			case ZEND_SR:
6398			case ZEND_MOD:
6399				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
6400						IS_CV, opline->op1, var_addr, var_info, NULL,
6401						(opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info,
6402						op1_data_range,
6403						0, var_addr, var_def_info, var_info, may_throw)) {
6404					return 0;
6405				}
6406				break;
6407			case ZEND_CONCAT:
6408				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,
6409						may_throw)) {
6410					return 0;
6411				}
6412				break;
6413			default:
6414				ZEND_UNREACHABLE();
6415		}
6416		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
6417	}
6418
6419	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
6420		binary_op_type binary_op;
6421
6422		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6423			|.cold_code
6424			|7:
6425		}
6426
6427		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
6428			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
6429		}
6430	    if (opline->op2_type == IS_UNUSED) {
6431			|	mov FCARG2x, xzr
6432		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
6433			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
6434			|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
6435		} else {
6436			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
6437		}
6438		binary_op = get_binary_op(opline->extended_value);
6439		|	LOAD_ZVAL_ADDR CARG3, op3_addr
6440		|	LOAD_ADDR CARG4, binary_op
6441		|	EXT_CALL zend_jit_assign_dim_op_helper, REG0
6442
6443		|9:
6444		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
6445		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
6446		if (may_throw) {
6447			zend_jit_check_exception(Dst);
6448		}
6449
6450		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
6451			|	b >9 // END
6452			|.code
6453			|9:
6454		}
6455	} else if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY))
6456			&& (!not_found_exit_addr || (var_info & MAY_BE_REF))) {
6457		|.cold_code
6458		|9:
6459		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, opline, ZREG_TMP1, ZREG_TMP2
6460		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
6461		if (may_throw) {
6462			zend_jit_check_exception(Dst);
6463		}
6464		|	b >9
6465		|.code
6466		|9:
6467	}
6468
6469	return 1;
6470}
6471
6472static 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)
6473{
6474	zend_jit_addr op1_addr, op2_addr;
6475
6476	ZEND_ASSERT(opline->op1_type == IS_CV && opline->result_type == IS_UNUSED);
6477	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF));
6478
6479	op1_addr = OP1_ADDR();
6480	op2_addr = OP2_ADDR();
6481
6482	if (op1_info & MAY_BE_REF) {
6483		binary_op_type binary_op = get_binary_op(opline->extended_value);
6484		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
6485		|	IF_NOT_Z_TYPE, FCARG1x, IS_REFERENCE, >1, TMP1w
6486		|	GET_Z_PTR FCARG1x, FCARG1x
6487		|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
6488		|	cbnz TMP1, >2
6489		|	add FCARG1x, FCARG1x, #offsetof(zend_reference, val)
6490		|.cold_code
6491		|2:
6492		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
6493		|	LOAD_ADDR CARG3, binary_op
6494		|	SET_EX_OPLINE opline, REG0
6495		if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
6496		 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
6497			|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0
6498		} else {
6499			|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
6500		}
6501		zend_jit_check_exception(Dst);
6502		|	b >9
6503		|.code
6504		|1:
6505		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
6506	}
6507
6508	int result;
6509	switch (opline->extended_value) {
6510		case ZEND_ADD:
6511		case ZEND_SUB:
6512		case ZEND_MUL:
6513		case ZEND_DIV:
6514			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);
6515			break;
6516		case ZEND_BW_OR:
6517		case ZEND_BW_AND:
6518		case ZEND_BW_XOR:
6519		case ZEND_SL:
6520		case ZEND_SR:
6521		case ZEND_MOD:
6522			result = zend_jit_long_math_helper(Dst, opline, opline->extended_value,
6523				opline->op1_type, opline->op1, op1_addr, op1_info, op1_range,
6524				opline->op2_type, opline->op2, op2_addr, op2_info, op2_range,
6525				opline->op1.var, op1_addr, op1_def_info, op1_info, may_throw);
6526			break;
6527		case ZEND_CONCAT:
6528			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);
6529			break;
6530		default:
6531			ZEND_UNREACHABLE();
6532	}
6533	|9:
6534	return result;
6535}
6536
6537static int zend_jit_cmp_long_long(dasm_State    **Dst,
6538                                  const zend_op  *opline,
6539                                  zend_ssa_range *op1_range,
6540                                  zend_jit_addr   op1_addr,
6541                                  zend_ssa_range *op2_range,
6542                                  zend_jit_addr   op2_addr,
6543                                  zend_jit_addr   res_addr,
6544                                  zend_uchar      smart_branch_opcode,
6545                                  uint32_t        target_label,
6546                                  uint32_t        target_label2,
6547                                  const void     *exit_addr,
6548                                  bool            skip_comparison)
6549{
6550	bool swap = 0;
6551	bool result;
6552
6553	if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) {
6554		if (!smart_branch_opcode ||
6555		    smart_branch_opcode == ZEND_JMPZ_EX ||
6556		    smart_branch_opcode == ZEND_JMPNZ_EX) {
6557			|	SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE), TMP1w, TMP2
6558		}
6559		if (smart_branch_opcode && !exit_addr) {
6560			if (smart_branch_opcode == ZEND_JMPZ ||
6561			    smart_branch_opcode == ZEND_JMPZ_EX) {
6562				if (!result) {
6563					|	b => target_label
6564				}
6565			} else if (smart_branch_opcode == ZEND_JMPNZ ||
6566			           smart_branch_opcode == ZEND_JMPNZ_EX) {
6567				if (result) {
6568					|	b => target_label
6569				}
6570			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
6571				if (!result) {
6572					|	b => target_label
6573				} else {
6574					|	b => target_label2
6575				}
6576			} else {
6577				ZEND_UNREACHABLE();
6578			}
6579		}
6580		return 1;
6581	}
6582
6583	if (skip_comparison) {
6584		if (Z_MODE(op1_addr) != IS_REG &&
6585		    (Z_MODE(op2_addr) == IS_REG ||
6586		     (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL))) {
6587			swap = 1;
6588		}
6589	} else if (Z_MODE(op1_addr) == IS_REG) {
6590		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
6591			|	cmp Rx(Z_REG(op1_addr)), xzr
6592		} else {
6593			|	LONG_CMP Z_REG(op1_addr), op2_addr, TMP1
6594		}
6595	} else if (Z_MODE(op2_addr) == IS_REG) {
6596		if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 0) {
6597			|	cmp Rx(Z_REG(op2_addr)), xzr
6598		} else {
6599			|	LONG_CMP Z_REG(op2_addr), op1_addr, TMP1
6600		}
6601		swap = 1;
6602	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL) {
6603		|	LONG_CMP_WITH_CONST op2_addr, Z_LVAL_P(Z_ZV(op1_addr)), TMP1, TMP2
6604		swap = 1;
6605	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_MODE(op1_addr) != IS_CONST_ZVAL) {
6606		|	LONG_CMP_WITH_CONST op1_addr, Z_LVAL_P(Z_ZV(op2_addr)), TMP1, TMP2
6607	} else {
6608		|	GET_ZVAL_LVAL ZREG_REG0, op1_addr, TMP1
6609		if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
6610			|	cmp Rx(ZREG_REG0), xzr
6611		} else {
6612			|	LONG_CMP ZREG_REG0, op2_addr, TMP1
6613		}
6614	}
6615
6616	if (smart_branch_opcode) {
6617		if (smart_branch_opcode == ZEND_JMPZ_EX ||
6618		    smart_branch_opcode == ZEND_JMPNZ_EX) {
6619
6620			switch (opline->opcode) {
6621				case ZEND_IS_EQUAL:
6622				case ZEND_IS_IDENTICAL:
6623				case ZEND_CASE:
6624				case ZEND_CASE_STRICT:
6625					|	cset REG0w, eq
6626					break;
6627				case ZEND_IS_NOT_EQUAL:
6628				case ZEND_IS_NOT_IDENTICAL:
6629					|	cset REG0w, ne
6630					break;
6631				case ZEND_IS_SMALLER:
6632					if (swap) {
6633						|	cset REG0w, gt
6634					} else {
6635						|	cset REG0w, lt
6636					}
6637					break;
6638				case ZEND_IS_SMALLER_OR_EQUAL:
6639					if (swap) {
6640						|	cset REG0w, ge
6641					} else {
6642						|	cset REG0w, le
6643					}
6644					break;
6645				default:
6646					ZEND_UNREACHABLE();
6647			}
6648			|	add REG0w, REG0w, #2
6649			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
6650		}
6651		if (smart_branch_opcode == ZEND_JMPZ ||
6652		    smart_branch_opcode == ZEND_JMPZ_EX) {
6653			switch (opline->opcode) {
6654				case ZEND_IS_EQUAL:
6655				case ZEND_IS_IDENTICAL:
6656				case ZEND_CASE:
6657				case ZEND_CASE_STRICT:
6658					if (exit_addr) {
6659						|	bne &exit_addr
6660					} else {
6661						|	bne => target_label
6662					}
6663					break;
6664				case ZEND_IS_NOT_EQUAL:
6665					if (exit_addr) {
6666						|	beq &exit_addr
6667					} else {
6668						|	beq => target_label
6669					}
6670					break;
6671				case ZEND_IS_NOT_IDENTICAL:
6672					if (exit_addr) {
6673						|	bne &exit_addr
6674					} else {
6675						|	beq => target_label
6676					}
6677					break;
6678				case ZEND_IS_SMALLER:
6679					if (swap) {
6680						if (exit_addr) {
6681							|	ble &exit_addr
6682						} else {
6683							|	ble => target_label
6684						}
6685					} else {
6686						if (exit_addr) {
6687							|	bge &exit_addr
6688						} else {
6689							|	bge => target_label
6690						}
6691					}
6692					break;
6693				case ZEND_IS_SMALLER_OR_EQUAL:
6694					if (swap) {
6695						if (exit_addr) {
6696							|	blt &exit_addr
6697						} else {
6698							|	blt => target_label
6699						}
6700					} else {
6701						if (exit_addr) {
6702							|	bgt &exit_addr
6703						} else {
6704							|	bgt => target_label
6705						}
6706					}
6707					break;
6708				default:
6709					ZEND_UNREACHABLE();
6710			}
6711		} else if (smart_branch_opcode == ZEND_JMPNZ ||
6712		           smart_branch_opcode == ZEND_JMPNZ_EX) {
6713			switch (opline->opcode) {
6714				case ZEND_IS_EQUAL:
6715				case ZEND_IS_IDENTICAL:
6716				case ZEND_CASE:
6717				case ZEND_CASE_STRICT:
6718					if (exit_addr) {
6719						|	beq &exit_addr
6720					} else {
6721						|	beq => target_label
6722					}
6723					break;
6724				case ZEND_IS_NOT_EQUAL:
6725					if (exit_addr) {
6726						|	bne &exit_addr
6727					} else {
6728						|	bne => target_label
6729					}
6730					break;
6731				case ZEND_IS_NOT_IDENTICAL:
6732					if (exit_addr) {
6733						|	beq &exit_addr
6734					} else {
6735						|	bne => target_label
6736					}
6737					break;
6738				case ZEND_IS_SMALLER:
6739					if (swap) {
6740						if (exit_addr) {
6741							|	bgt &exit_addr
6742						} else {
6743							|	bgt => target_label
6744						}
6745					} else {
6746						if (exit_addr) {
6747							|	blt &exit_addr
6748						} else {
6749							|	blt => target_label
6750						}
6751					}
6752					break;
6753				case ZEND_IS_SMALLER_OR_EQUAL:
6754					if (swap) {
6755						if (exit_addr) {
6756							|	bge &exit_addr
6757						} else {
6758							|	bge => target_label
6759						}
6760					} else {
6761						if (exit_addr) {
6762							|	ble &exit_addr
6763						} else {
6764							|	ble => target_label
6765						}
6766					}
6767					break;
6768				default:
6769					ZEND_UNREACHABLE();
6770			}
6771		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
6772			switch (opline->opcode) {
6773				case ZEND_IS_EQUAL:
6774				case ZEND_IS_IDENTICAL:
6775				case ZEND_CASE:
6776				case ZEND_CASE_STRICT:
6777					|	bne => target_label
6778					break;
6779				case ZEND_IS_NOT_EQUAL:
6780				case ZEND_IS_NOT_IDENTICAL:
6781					|	beq => target_label
6782					break;
6783				case ZEND_IS_SMALLER:
6784				    if (swap) {
6785						|	ble => target_label
6786				    } else {
6787						|	bge => target_label
6788					}
6789					break;
6790				case ZEND_IS_SMALLER_OR_EQUAL:
6791					if (swap) {
6792						|	blt => target_label
6793					} else {
6794						|	bgt => target_label
6795					}
6796					break;
6797				default:
6798					ZEND_UNREACHABLE();
6799			}
6800			|	b => target_label2
6801		} else {
6802			ZEND_UNREACHABLE();
6803		}
6804	} else {
6805		switch (opline->opcode) {
6806			case ZEND_IS_EQUAL:
6807			case ZEND_IS_IDENTICAL:
6808			case ZEND_CASE:
6809			case ZEND_CASE_STRICT:
6810				|	cset REG0w, eq
6811				break;
6812			case ZEND_IS_NOT_EQUAL:
6813			case ZEND_IS_NOT_IDENTICAL:
6814				|	cset REG0w, ne
6815				break;
6816			case ZEND_IS_SMALLER:
6817				if (swap) {
6818					|	cset REG0w, gt
6819				} else {
6820					|	cset REG0w, lt
6821				}
6822				break;
6823			case ZEND_IS_SMALLER_OR_EQUAL:
6824				if (swap) {
6825					|	cset REG0w, ge
6826				} else {
6827					|	cset REG0w, le
6828				}
6829				break;
6830			default:
6831				ZEND_UNREACHABLE();
6832		}
6833		|	add REG0w, REG0w, #2
6834		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
6835	}
6836
6837	return 1;
6838}
6839
6840static 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)
6841{
6842	if (smart_branch_opcode) {
6843		if (smart_branch_opcode == ZEND_JMPZ) {
6844			switch (opline->opcode) {
6845				case ZEND_IS_EQUAL:
6846				case ZEND_IS_IDENTICAL:
6847				case ZEND_CASE:
6848				case ZEND_CASE_STRICT:
6849					if (exit_addr) {
6850						|	bne &exit_addr
6851					} else {
6852						|	bne => target_label
6853					}
6854					break;
6855				case ZEND_IS_NOT_EQUAL:
6856					|	bvs >1
6857					if (exit_addr) {
6858						|	beq &exit_addr
6859					} else {
6860						|	beq => target_label
6861					}
6862					|1:
6863					break;
6864				case ZEND_IS_NOT_IDENTICAL:
6865					if (exit_addr) {
6866						|	bvs &exit_addr
6867						|	bne &exit_addr
6868					} else {
6869						|	bvs >1
6870						|	beq => target_label
6871						|1:
6872					}
6873					break;
6874				case ZEND_IS_SMALLER:
6875					if (swap) {
6876						if (exit_addr) {
6877							|	bvs &exit_addr
6878							|	bls &exit_addr
6879						} else {
6880							|	bvs => target_label
6881							|	bls => target_label
6882						}
6883					} else {
6884						if (exit_addr) {
6885							|	bhs &exit_addr
6886						} else {
6887							|	bhs => target_label
6888						}
6889					}
6890					break;
6891				case ZEND_IS_SMALLER_OR_EQUAL:
6892					if (swap) {
6893						if (exit_addr) {
6894							|	bvs &exit_addr
6895							|	blo &exit_addr
6896						} else {
6897							|	bvs => target_label
6898							|	blo => target_label
6899						}
6900					} else {
6901						if (exit_addr) {
6902							|	bhi &exit_addr
6903						} else {
6904							|	bhi => target_label
6905						}
6906					}
6907					break;
6908				default:
6909					ZEND_UNREACHABLE();
6910			}
6911		} else if (smart_branch_opcode == ZEND_JMPNZ) {
6912			switch (opline->opcode) {
6913				case ZEND_IS_EQUAL:
6914				case ZEND_IS_IDENTICAL:
6915				case ZEND_CASE:
6916				case ZEND_CASE_STRICT:
6917					|	bvs >1
6918					if (exit_addr) {
6919						|	beq &exit_addr
6920					} else {
6921						|	beq => target_label
6922					}
6923					|1:
6924					break;
6925				case ZEND_IS_NOT_EQUAL:
6926					if (exit_addr) {
6927						|	bne &exit_addr
6928					} else {
6929						|	bne => target_label
6930					}
6931					break;
6932				case ZEND_IS_NOT_IDENTICAL:
6933					if (exit_addr) {
6934						|	bvs >1
6935						|	beq &exit_addr
6936						|1:
6937					} else {
6938						|	bne => target_label
6939					}
6940					break;
6941				case ZEND_IS_SMALLER:
6942					if (swap) {
6943						|	bvs >1  // Always False if involving NaN
6944						if (exit_addr) {
6945							|	bhi &exit_addr
6946						} else {
6947							|	bhi => target_label
6948						}
6949						|1:
6950					} else {
6951						|	bvs >1
6952						if (exit_addr) {
6953							|	blo	&exit_addr
6954						} else {
6955							|	blo => target_label
6956						}
6957						|1:
6958					}
6959					break;
6960				case ZEND_IS_SMALLER_OR_EQUAL:
6961					if (swap) {
6962						|	bvs >1  // Always False if involving NaN
6963						if (exit_addr) {
6964							|	bhs &exit_addr
6965						} else {
6966							|	bhs => target_label
6967						}
6968						|1:
6969					} else {
6970						|	bvs >1
6971						if (exit_addr) {
6972							|	bls &exit_addr
6973						} else {
6974							|	bls => target_label
6975						}
6976						|1:
6977					}
6978					break;
6979				default:
6980					ZEND_UNREACHABLE();
6981			}
6982		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
6983			switch (opline->opcode) {
6984				case ZEND_IS_EQUAL:
6985				case ZEND_IS_IDENTICAL:
6986				case ZEND_CASE:
6987				case ZEND_CASE_STRICT:
6988					|	bne => target_label
6989					break;
6990				case ZEND_IS_NOT_EQUAL:
6991				case ZEND_IS_NOT_IDENTICAL:
6992					|	bvs => target_label2
6993					|	beq => target_label
6994					break;
6995				case ZEND_IS_SMALLER:
6996					if (swap) {
6997						|	bvs => target_label
6998						|	bls => target_label
6999					} else {
7000						|	bhs => target_label
7001					}
7002					break;
7003				case ZEND_IS_SMALLER_OR_EQUAL:
7004					if (swap) {
7005						|	bvs => target_label
7006						|	blo => target_label
7007					} else {
7008						|	bhi => target_label
7009					}
7010					break;
7011				default:
7012					ZEND_UNREACHABLE();
7013			}
7014			|	b => target_label2
7015		} else if (smart_branch_opcode == ZEND_JMPZ_EX) {
7016			switch (opline->opcode) {
7017				case ZEND_IS_EQUAL:
7018				case ZEND_IS_IDENTICAL:
7019				case ZEND_CASE:
7020				case ZEND_CASE_STRICT:
7021					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7022					|	bne => target_label
7023					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7024					break;
7025				case ZEND_IS_NOT_EQUAL:
7026				case ZEND_IS_NOT_IDENTICAL:
7027					|	bvs >1
7028					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7029					|	beq => target_label
7030					|1:
7031					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7032					break;
7033				case ZEND_IS_SMALLER:
7034					if (swap) {
7035						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7036						|	bvs => target_label
7037						|	bls => target_label
7038						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7039					} else {
7040						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7041						|	bhs => target_label
7042						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7043					}
7044					break;
7045				case ZEND_IS_SMALLER_OR_EQUAL:
7046					if (swap) {
7047						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7048						|	bvs => target_label
7049						|	blo => target_label
7050						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7051					} else {
7052						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7053						|	bhi => target_label
7054						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7055					}
7056					break;
7057				default:
7058					ZEND_UNREACHABLE();
7059			}
7060		} else if (smart_branch_opcode == ZEND_JMPNZ_EX) {
7061			switch (opline->opcode) {
7062				case ZEND_IS_EQUAL:
7063				case ZEND_IS_IDENTICAL:
7064				case ZEND_CASE:
7065				case ZEND_CASE_STRICT:
7066					|	bvs >1
7067					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7068					|	beq => target_label
7069					|1:
7070					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7071					break;
7072				case ZEND_IS_NOT_EQUAL:
7073				case ZEND_IS_NOT_IDENTICAL:
7074					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7075					|	bvs => target_label
7076					|	bne => target_label
7077					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7078					break;
7079				case ZEND_IS_SMALLER:
7080					if (swap) {
7081						|	cset REG0w, hi
7082						|	add REG0w, REG0w, #2
7083						|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
7084						|	bvs >1  // Always False if involving NaN
7085						|	bhi => target_label
7086						|1:
7087					} else {
7088						|	bvs >1
7089						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7090						|	blo => target_label
7091						|1:
7092						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7093					}
7094					break;
7095				case ZEND_IS_SMALLER_OR_EQUAL:
7096					if (swap) {
7097						|	cset REG0w, hs
7098						|	add REG0w, REG0w, #2
7099						|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
7100						|	bvs >1  // Always False if involving NaN
7101						|	bhs => target_label
7102						|1:
7103					} else {
7104						|	bvs >1
7105						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
7106						|	bls => target_label
7107						|1:
7108						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
7109					}
7110					break;
7111				default:
7112					ZEND_UNREACHABLE();
7113			}
7114		} else {
7115			ZEND_UNREACHABLE();
7116		}
7117	} else {
7118		switch (opline->opcode) {
7119			case ZEND_IS_EQUAL:
7120			case ZEND_IS_IDENTICAL:
7121			case ZEND_CASE:
7122			case ZEND_CASE_STRICT:
7123				|	bvs >1
7124				|	mov REG0, #IS_TRUE
7125				|	beq >2
7126				|1:
7127				|	mov REG0, #IS_FALSE
7128				|2:
7129				break;
7130			case ZEND_IS_NOT_EQUAL:
7131			case ZEND_IS_NOT_IDENTICAL:
7132				|	bvs >1
7133				|	mov REG0, #IS_FALSE
7134				|	beq >2
7135				|1:
7136				|	mov REG0, #IS_TRUE
7137				|2:
7138				break;
7139			case ZEND_IS_SMALLER:
7140				|	bvs >1
7141				|	mov REG0, #IS_TRUE
7142				||	if (swap) {
7143				|		bhi >2
7144				||	} else {
7145				|		blo >2
7146				||	}
7147				|1:
7148				|	mov REG0, #IS_FALSE
7149				|2:
7150				break;
7151			case ZEND_IS_SMALLER_OR_EQUAL:
7152				|	bvs >1
7153				|	mov REG0, #IS_TRUE
7154				||	if (swap) {
7155				|		bhs >2
7156				||	} else {
7157				|		bls >2
7158				||	}
7159				|1:
7160				|	mov REG0, #IS_FALSE
7161				|2:
7162				break;
7163			default:
7164				ZEND_UNREACHABLE();
7165		}
7166		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
7167	}
7168
7169	return 1;
7170}
7171
7172static 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)
7173{
7174	zend_reg tmp_reg = ZREG_FPR0;
7175
7176	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op1_addr, ZREG_REG0, ZREG_TMP1
7177	|	DOUBLE_CMP tmp_reg, op2_addr, ZREG_TMP1, ZREG_FPTMP
7178
7179	return zend_jit_cmp_double_common(Dst, opline, res_addr, 0, smart_branch_opcode, target_label, target_label2, exit_addr);
7180}
7181
7182static 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)
7183{
7184	zend_reg tmp_reg = ZREG_FPR0;
7185
7186	|	DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, ZREG_REG0, ZREG_TMP1
7187	|	DOUBLE_CMP tmp_reg, op1_addr, ZREG_TMP1, ZREG_FPTMP
7188
7189	return zend_jit_cmp_double_common(Dst, opline, res_addr, /* swap */ 1, smart_branch_opcode, target_label, target_label2, exit_addr);
7190}
7191
7192static 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)
7193{
7194	bool swap = 0;
7195
7196	if (Z_MODE(op1_addr) == IS_REG) {
7197		|	DOUBLE_CMP Z_REG(op1_addr), op2_addr, ZREG_TMP1, ZREG_FPTMP
7198	} else if (Z_MODE(op2_addr) == IS_REG) {
7199		|	DOUBLE_CMP Z_REG(op2_addr), op1_addr, ZREG_TMP1, ZREG_FPTMP
7200		swap = 1;
7201	} else {
7202		zend_reg tmp_reg = ZREG_FPR0;
7203
7204		|	GET_ZVAL_DVAL tmp_reg, op1_addr, ZREG_TMP1
7205		|	DOUBLE_CMP tmp_reg, op2_addr, ZREG_TMP1, ZREG_FPTMP
7206	}
7207
7208	return zend_jit_cmp_double_common(Dst, opline, res_addr, swap, smart_branch_opcode, target_label, target_label2, exit_addr);
7209}
7210
7211static 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)
7212{
7213	|	tst RETVALw, RETVALw
7214	if (smart_branch_opcode) {
7215		if (smart_branch_opcode == ZEND_JMPZ_EX ||
7216		    smart_branch_opcode == ZEND_JMPNZ_EX) {
7217			switch (opline->opcode) {
7218				case ZEND_IS_EQUAL:
7219				case ZEND_CASE:
7220					|	cset REG0w, eq
7221					break;
7222				case ZEND_IS_NOT_EQUAL:
7223					|	cset REG0w, ne
7224					break;
7225				case ZEND_IS_SMALLER:
7226					|	cset REG0w, lt
7227					break;
7228				case ZEND_IS_SMALLER_OR_EQUAL:
7229					|	cset REG0w, le
7230					break;
7231				default:
7232					ZEND_UNREACHABLE();
7233			}
7234			|	add REG0w, REG0w, #2
7235			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
7236		}
7237		if (smart_branch_opcode == ZEND_JMPZ ||
7238		    smart_branch_opcode == ZEND_JMPZ_EX) {
7239			switch (opline->opcode) {
7240				case ZEND_IS_EQUAL:
7241				case ZEND_CASE:
7242					if (exit_addr) {
7243						|	bne &exit_addr
7244					} else {
7245						|	bne => target_label
7246					}
7247					break;
7248				case ZEND_IS_NOT_EQUAL:
7249					if (exit_addr) {
7250						|	beq &exit_addr
7251					} else {
7252						|	beq => target_label
7253					}
7254					break;
7255				case ZEND_IS_SMALLER:
7256					if (exit_addr) {
7257						|	bge &exit_addr
7258					} else {
7259						|	bge => target_label
7260					}
7261					break;
7262				case ZEND_IS_SMALLER_OR_EQUAL:
7263					if (exit_addr) {
7264						|	bgt &exit_addr
7265					} else {
7266						|	bgt => target_label
7267					}
7268					break;
7269				default:
7270					ZEND_UNREACHABLE();
7271			}
7272		} else if (smart_branch_opcode == ZEND_JMPNZ ||
7273		           smart_branch_opcode == ZEND_JMPNZ_EX) {
7274			switch (opline->opcode) {
7275				case ZEND_IS_EQUAL:
7276				case ZEND_CASE:
7277					if (exit_addr) {
7278						|	beq &exit_addr
7279					} else {
7280						|	beq => target_label
7281					}
7282					break;
7283				case ZEND_IS_NOT_EQUAL:
7284					if (exit_addr) {
7285						|	bne &exit_addr
7286					} else {
7287						|	bne => target_label
7288					}
7289					break;
7290				case ZEND_IS_SMALLER:
7291					if (exit_addr) {
7292						|	blt &exit_addr
7293					} else {
7294						|	blt => target_label
7295					}
7296					break;
7297				case ZEND_IS_SMALLER_OR_EQUAL:
7298					if (exit_addr) {
7299						|	ble &exit_addr
7300					} else {
7301						|	ble => target_label
7302					}
7303					break;
7304				default:
7305					ZEND_UNREACHABLE();
7306			}
7307		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
7308			switch (opline->opcode) {
7309				case ZEND_IS_EQUAL:
7310				case ZEND_CASE:
7311					|	bne => target_label
7312					break;
7313				case ZEND_IS_NOT_EQUAL:
7314					|	beq => target_label
7315					break;
7316				case ZEND_IS_SMALLER:
7317					|	bge => target_label
7318					break;
7319				case ZEND_IS_SMALLER_OR_EQUAL:
7320					|	bgt => target_label
7321					break;
7322				default:
7323					ZEND_UNREACHABLE();
7324			}
7325			|	b => target_label2
7326		} else {
7327			ZEND_UNREACHABLE();
7328		}
7329	} else {
7330		switch (opline->opcode) {
7331			case ZEND_IS_EQUAL:
7332			case ZEND_CASE:
7333				|	cset REG0w, eq
7334				break;
7335			case ZEND_IS_NOT_EQUAL:
7336				|	cset REG0w, ne
7337				break;
7338			case ZEND_IS_SMALLER:
7339				|	cset REG0w, lt
7340				break;
7341			case ZEND_IS_SMALLER_OR_EQUAL:
7342				|	cset REG0w, le
7343				break;
7344			default:
7345				ZEND_UNREACHABLE();
7346		}
7347		|	add REG0w, REG0w, #2
7348		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
7349	}
7350
7351	return 1;
7352}
7353
7354static int zend_jit_cmp(dasm_State    **Dst,
7355                        const zend_op  *opline,
7356                        uint32_t        op1_info,
7357                        zend_ssa_range *op1_range,
7358                        zend_jit_addr   op1_addr,
7359                        uint32_t        op2_info,
7360                        zend_ssa_range *op2_range,
7361                        zend_jit_addr   op2_addr,
7362                        zend_jit_addr   res_addr,
7363                        int             may_throw,
7364                        zend_uchar      smart_branch_opcode,
7365                        uint32_t        target_label,
7366                        uint32_t        target_label2,
7367                        const void     *exit_addr,
7368                        bool            skip_comparison)
7369{
7370	bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
7371	bool has_slow;
7372
7373	has_slow =
7374		(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
7375		(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
7376		((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
7377		 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))));
7378
7379	if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
7380		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
7381			if (op1_info & MAY_BE_DOUBLE) {
7382				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >4, ZREG_TMP1
7383			} else {
7384				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9, ZREG_TMP1
7385			}
7386		}
7387		if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) {
7388			if (op2_info & MAY_BE_DOUBLE) {
7389				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3, ZREG_TMP1
7390				|.cold_code
7391				|3:
7392				if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
7393					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
7394				}
7395				if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7396					return 0;
7397				}
7398				|	b >6
7399				|.code
7400			} else {
7401				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1
7402			}
7403		}
7404		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)) {
7405			return 0;
7406		}
7407		if (op1_info & MAY_BE_DOUBLE) {
7408			|.cold_code
7409			|4:
7410			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
7411				|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1
7412			}
7413			if (op2_info & MAY_BE_DOUBLE) {
7414				if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
7415					if (!same_ops) {
7416						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >5, ZREG_TMP1
7417					} else {
7418						|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
7419					}
7420				}
7421				if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7422					return 0;
7423				}
7424				|	b >6
7425			}
7426			if (!same_ops) {
7427				|5:
7428				if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) {
7429					|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1
7430				}
7431				if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7432					return 0;
7433				}
7434				|	b >6
7435			}
7436			|.code
7437		}
7438	} else if ((op1_info & MAY_BE_DOUBLE) &&
7439	           !(op1_info & MAY_BE_LONG) &&
7440	           (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
7441		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) {
7442			|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1
7443		}
7444		if (op2_info & MAY_BE_DOUBLE) {
7445			if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
7446				if (!same_ops && (op2_info & MAY_BE_LONG)) {
7447					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >3, ZREG_TMP1
7448				} else {
7449					|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
7450				}
7451			}
7452			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7453				return 0;
7454			}
7455		}
7456		if (!same_ops && (op2_info & MAY_BE_LONG)) {
7457			if (op2_info & MAY_BE_DOUBLE) {
7458				|.cold_code
7459			}
7460		    |3:
7461			if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
7462				|	IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1
7463			}
7464			if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7465				return 0;
7466			}
7467			if (op2_info & MAY_BE_DOUBLE) {
7468				|	b >6
7469				|.code
7470			}
7471		}
7472	} else if ((op2_info & MAY_BE_DOUBLE) &&
7473	           !(op2_info & MAY_BE_LONG) &&
7474	           (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
7475		if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) {
7476			|	IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1
7477		}
7478		if (op1_info & MAY_BE_DOUBLE) {
7479			if (!same_ops && (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
7480				if (!same_ops && (op1_info & MAY_BE_LONG)) {
7481					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >3, ZREG_TMP1
7482				} else {
7483					|	IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1
7484				}
7485			}
7486			if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7487				return 0;
7488			}
7489		}
7490		if (!same_ops && (op1_info & MAY_BE_LONG)) {
7491			if (op1_info & MAY_BE_DOUBLE) {
7492				|.cold_code
7493			}
7494			|3:
7495			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) {
7496				|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9, ZREG_TMP1
7497			}
7498			if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7499				return 0;
7500			}
7501			if (op1_info & MAY_BE_DOUBLE) {
7502				|	b >6
7503				|.code
7504			}
7505		}
7506	}
7507
7508	if (has_slow ||
7509	    (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) ||
7510	    (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
7511		if (has_slow) {
7512			|.cold_code
7513			|9:
7514		}
7515		|	SET_EX_OPLINE opline, REG0
7516		if (Z_MODE(op1_addr) == IS_REG) {
7517			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
7518			if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
7519				return 0;
7520			}
7521			op1_addr = real_addr;
7522		}
7523		if (Z_MODE(op2_addr) == IS_REG) {
7524			zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
7525			if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
7526				return 0;
7527			}
7528			op2_addr = real_addr;
7529		}
7530		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
7531		if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) {
7532			|	IF_NOT_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w
7533			|	LOAD_32BIT_VAL FCARG1x, opline->op1.var
7534			|	EXT_CALL zend_jit_undefined_op_helper, REG0
7535			|	LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
7536			|1:
7537		}
7538		if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) {
7539			|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1
7540			|	str FCARG1x, T1 // save
7541			|	LOAD_32BIT_VAL FCARG1x, opline->op2.var
7542			|	EXT_CALL zend_jit_undefined_op_helper, REG0
7543			|	ldr FCARG1x, T1 // restore
7544			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
7545			|	b >2
7546			|1:
7547			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
7548			|2:
7549		} else {
7550			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
7551		}
7552		|	EXT_CALL zend_compare, REG0
7553		if ((opline->opcode != ZEND_CASE &&
7554		     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
7555		     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
7556		    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
7557		     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
7558			|	str RETVALw, T1 // save
7559			if (opline->opcode != ZEND_CASE) {
7560				|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
7561			}
7562			|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2
7563			|	ldr RETVALw, T1 // restore
7564		}
7565		if (may_throw) {
7566			zend_jit_check_exception_undef_result(Dst, opline);
7567		}
7568		if (!zend_jit_cmp_slow(Dst, opline, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7569			return 0;
7570		}
7571		if (has_slow) {
7572			|	b >6
7573			|.code
7574		}
7575	}
7576
7577	|6:
7578
7579	return 1;
7580}
7581
7582static int zend_jit_identical(dasm_State    **Dst,
7583                              const zend_op  *opline,
7584                              uint32_t        op1_info,
7585                              zend_ssa_range *op1_range,
7586                              zend_jit_addr   op1_addr,
7587                              uint32_t        op2_info,
7588                              zend_ssa_range *op2_range,
7589                              zend_jit_addr   op2_addr,
7590                              zend_jit_addr   res_addr,
7591                              int             may_throw,
7592                              zend_uchar      smart_branch_opcode,
7593                              uint32_t        target_label,
7594                              uint32_t        target_label2,
7595                              const void     *exit_addr,
7596                              bool            skip_comparison)
7597{
7598	uint32_t identical_label = (uint32_t)-1;
7599	uint32_t not_identical_label = (uint32_t)-1;
7600
7601	if (smart_branch_opcode && !exit_addr) {
7602		if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
7603			if (smart_branch_opcode == ZEND_JMPZ) {
7604				not_identical_label = target_label;
7605			} else if (smart_branch_opcode == ZEND_JMPNZ) {
7606				identical_label = target_label;
7607			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
7608				not_identical_label = target_label;
7609				identical_label = target_label2;
7610			} else {
7611				ZEND_UNREACHABLE();
7612			}
7613		} else {
7614			if (smart_branch_opcode == ZEND_JMPZ) {
7615				identical_label = target_label;
7616			} else if (smart_branch_opcode == ZEND_JMPNZ) {
7617				not_identical_label = target_label;
7618			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
7619				identical_label = target_label;
7620				not_identical_label = target_label2;
7621			} else {
7622				ZEND_UNREACHABLE();
7623			}
7624		}
7625	}
7626
7627	if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
7628	    (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
7629		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)) {
7630			return 0;
7631		}
7632		return 1;
7633	} else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE &&
7634	           (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) {
7635		if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
7636			return 0;
7637		}
7638		return 1;
7639	}
7640
7641	if ((op1_info & MAY_BE_UNDEF) && (op2_info & MAY_BE_UNDEF)) {
7642		op1_info |= MAY_BE_NULL;
7643		op2_info |= MAY_BE_NULL;
7644		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
7645		|	IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w
7646		|.cold_code
7647		|1:
7648		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
7649		|	SET_EX_OPLINE opline, REG0
7650		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
7651		|	EXT_CALL zend_jit_undefined_op_helper, REG0
7652		if (may_throw) {
7653			zend_jit_check_exception_undef_result(Dst, opline);
7654		}
7655		|	LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
7656		|	b >1
7657		|.code
7658		|1:
7659		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
7660		|	IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w
7661		|.cold_code
7662		|1:
7663		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
7664		|	SET_EX_OPLINE opline, REG0
7665		|	str FCARG1x, T1 // save
7666		|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
7667		|	EXT_CALL zend_jit_undefined_op_helper, REG0
7668		if (may_throw) {
7669			zend_jit_check_exception_undef_result(Dst, opline);
7670		}
7671		|	ldr FCARG1x, T1 // restore
7672		|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
7673		|	b >1
7674		|.code
7675		|1:
7676	} else if (op1_info & MAY_BE_UNDEF) {
7677		op1_info |= MAY_BE_NULL;
7678		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
7679		|	IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w
7680		|.cold_code
7681		|1:
7682		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
7683		|	SET_EX_OPLINE opline, REG0
7684		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
7685		|	EXT_CALL zend_jit_undefined_op_helper, REG0
7686		if (may_throw) {
7687			zend_jit_check_exception_undef_result(Dst, opline);
7688		}
7689		|	LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
7690		|	b >1
7691		|.code
7692		|1:
7693		if (opline->op2_type != IS_CONST) {
7694			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
7695		}
7696	} else if (op2_info & MAY_BE_UNDEF) {
7697		op2_info |= MAY_BE_NULL;
7698		|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
7699		|	IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w
7700		|.cold_code
7701		|1:
7702		|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
7703		|	SET_EX_OPLINE opline, REG0
7704		|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
7705		|	EXT_CALL zend_jit_undefined_op_helper, REG0
7706		if (may_throw) {
7707			zend_jit_check_exception_undef_result(Dst, opline);
7708		}
7709		|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
7710		|	b >1
7711		|.code
7712		|1:
7713		if (opline->op1_type != IS_CONST) {
7714			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
7715		}
7716	} else if ((op1_info & op2_info & MAY_BE_ANY) != 0) {
7717		if (opline->op1_type != IS_CONST) {
7718			if (Z_MODE(op1_addr) == IS_REG) {
7719				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
7720				if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
7721					return 0;
7722				}
7723				op1_addr = real_addr;
7724			}
7725		}
7726		if (opline->op2_type != IS_CONST) {
7727			if (Z_MODE(op2_addr) == IS_REG) {
7728				zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
7729				if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
7730					return 0;
7731				}
7732				op2_addr = real_addr;
7733			}
7734			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
7735		}
7736		if (opline->op1_type != IS_CONST) {
7737			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
7738		}
7739	}
7740
7741	if ((op1_info & op2_info & MAY_BE_ANY) == 0) {
7742		if ((opline->opcode != ZEND_CASE_STRICT &&
7743		     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
7744		     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
7745		    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
7746		     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
7747			if (opline->opcode != ZEND_CASE_STRICT) {
7748				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7749			}
7750			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7751		}
7752		if (smart_branch_opcode) {
7753			if (may_throw) {
7754				zend_jit_check_exception_undef_result(Dst, opline);
7755			}
7756			if (exit_addr) {
7757				if (smart_branch_opcode == ZEND_JMPZ) {
7758					|	b &exit_addr
7759				}
7760			} else if (not_identical_label != (uint32_t)-1) {
7761				|	b =>not_identical_label
7762			}
7763		} else {
7764			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2
7765			if (may_throw) {
7766				zend_jit_check_exception(Dst);
7767			}
7768		}
7769		return 1;
7770	}
7771
7772	if (opline->op1_type & (IS_CV|IS_VAR)) {
7773		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
7774	}
7775	if (opline->op2_type & (IS_CV|IS_VAR)) {
7776		|	ZVAL_DEREF FCARG2x, op2_info, TMP1w
7777	}
7778
7779	if (has_concrete_type(op1_info)
7780	 && has_concrete_type(op2_info)
7781	 && concrete_type(op1_info) == concrete_type(op2_info)
7782	 && concrete_type(op1_info) <= IS_TRUE) {
7783		if (smart_branch_opcode) {
7784			if (exit_addr) {
7785				if (smart_branch_opcode == ZEND_JMPNZ) {
7786					|	b &exit_addr
7787				}
7788			} else if (identical_label != (uint32_t)-1) {
7789				|	b =>identical_label
7790			}
7791		} else {
7792			|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2
7793		}
7794	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) == IS_CONST_ZVAL) {
7795		if (zend_is_identical(Z_ZV(op1_addr), Z_ZV(op2_addr))) {
7796			if (smart_branch_opcode) {
7797				if (exit_addr) {
7798					if (smart_branch_opcode == ZEND_JMPNZ) {
7799						|	b &exit_addr
7800					}
7801				} else if (identical_label != (uint32_t)-1) {
7802					|	b =>identical_label
7803				}
7804			} else {
7805				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2
7806			}
7807		} else {
7808			if (smart_branch_opcode) {
7809				if (exit_addr) {
7810					if (smart_branch_opcode == ZEND_JMPZ) {
7811						|	b &exit_addr
7812					}
7813				} else if (not_identical_label != (uint32_t)-1) {
7814					|	b =>not_identical_label
7815				}
7816			} else {
7817				|	SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2
7818			}
7819		}
7820	} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op1_addr)) <= IS_TRUE) {
7821		zval *val = Z_ZV(op1_addr);
7822
7823		|	ldrb TMP1w, [FCARG2x, #offsetof(zval, u1.v.type)]
7824		|	cmp TMP1w, #Z_TYPE_P(val)
7825		if (smart_branch_opcode) {
7826			if (opline->op2_type == IS_VAR && (op2_info & MAY_BE_REF)) {
7827				|	bne >8
7828				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7829				if (may_throw) {
7830					zend_jit_check_exception_undef_result(Dst, opline);
7831				}
7832				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
7833					|	b &exit_addr
7834				} else if (identical_label != (uint32_t)-1) {
7835					|	b =>identical_label
7836				} else {
7837					|	b >9
7838				}
7839				|8:
7840			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
7841				|	beq &exit_addr
7842			} else if (identical_label != (uint32_t)-1) {
7843				|	beq =>identical_label
7844			} else {
7845				|	beq >9
7846			}
7847		} else {
7848			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
7849				|	cset REG0w, eq
7850			} else {
7851				|	cset REG0w, ne
7852			}
7853			|	add REG0w, REG0w, #2
7854			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
7855		}
7856		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
7857		    (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
7858			|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7859			if (may_throw) {
7860				zend_jit_check_exception_undef_result(Dst, opline);
7861			}
7862		}
7863		if (exit_addr) {
7864			if (smart_branch_opcode == ZEND_JMPZ) {
7865				|	b &exit_addr
7866			}
7867		} else if (smart_branch_opcode && not_identical_label != (uint32_t)-1) {
7868			|	b =>not_identical_label
7869		}
7870	} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op2_addr)) <= IS_TRUE) {
7871		zval *val = Z_ZV(op2_addr);
7872
7873		|	ldrb TMP1w, [FCARG1x, #offsetof(zval, u1.v.type)]
7874		|	cmp TMP1w, #Z_TYPE_P(val)
7875		if (smart_branch_opcode) {
7876			if (opline->opcode != ZEND_CASE_STRICT
7877			 && opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) {
7878				|	bne >8
7879				|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7880				if (may_throw) {
7881					zend_jit_check_exception_undef_result(Dst, opline);
7882				}
7883				if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
7884					|	b &exit_addr
7885				} else if (identical_label != (uint32_t)-1) {
7886					|	b =>identical_label
7887				} else {
7888					|	b >9
7889				}
7890				|8:
7891			} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
7892				|	beq &exit_addr
7893			} else if (identical_label != (uint32_t)-1) {
7894				|	beq =>identical_label
7895			} else {
7896				|	beq >9
7897			}
7898		} else {
7899			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
7900				|	cset REG0w, eq
7901			} else {
7902				|	cset REG0w, ne
7903			}
7904			|	add REG0w, REG0w, #2
7905			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
7906		}
7907		if (opline->opcode != ZEND_CASE_STRICT
7908		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
7909		    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
7910			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7911			if (may_throw) {
7912				zend_jit_check_exception_undef_result(Dst, opline);
7913			}
7914		}
7915		if (smart_branch_opcode) {
7916			if (exit_addr) {
7917				if (smart_branch_opcode == ZEND_JMPZ) {
7918					|	b &exit_addr
7919				}
7920			} else if (not_identical_label != (uint32_t)-1) {
7921				|	b =>not_identical_label
7922			}
7923		}
7924	} else {
7925		if (opline->op1_type == IS_CONST) {
7926			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
7927		}
7928		if (opline->op2_type == IS_CONST) {
7929			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
7930		}
7931		|	EXT_CALL zend_is_identical, REG0
7932			if ((opline->opcode != ZEND_CASE_STRICT &&
7933			     (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
7934			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
7935			    ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
7936			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
7937				|	str RETVALw, T1 // save
7938				if (opline->opcode != ZEND_CASE_STRICT) {
7939					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7940				}
7941				|	FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
7942				if (may_throw) {
7943					zend_jit_check_exception_undef_result(Dst, opline);
7944				}
7945				|	ldr RETVALw, T1 // restore
7946			}
7947		if (smart_branch_opcode) {
7948			if (exit_addr) {
7949				if (smart_branch_opcode == ZEND_JMPNZ) {
7950					|	cbnz RETVALw, &exit_addr
7951				} else {
7952					|	cbz RETVALw, &exit_addr
7953				}
7954			} else if (not_identical_label != (uint32_t)-1) {
7955				|	cbz RETVALw, =>not_identical_label
7956				if (identical_label != (uint32_t)-1) {
7957					|	b =>identical_label
7958				}
7959			} else if (identical_label != (uint32_t)-1) {
7960				|	cbnz RETVALw, =>identical_label
7961			}
7962		} else {
7963			if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
7964				|	add RETVALw, RETVALw, #2
7965			} else {
7966				|	neg RETVALw, RETVALw
7967				|	add RETVALw, RETVALw, #3
7968			}
7969			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, RETVALw, TMP1
7970		}
7971	}
7972
7973	|9:
7974	if (may_throw) {
7975		zend_jit_check_exception(Dst);
7976	}
7977	return 1;
7978}
7979
7980static 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)
7981{
7982	uint32_t true_label = -1;
7983	uint32_t false_label = -1;
7984	bool set_bool = 0;
7985	bool set_bool_not = 0;
7986	bool set_delayed = 0;
7987	bool jmp_done = 0;
7988
7989	if (branch_opcode == ZEND_BOOL) {
7990		set_bool = 1;
7991	} else if (branch_opcode == ZEND_BOOL_NOT) {
7992		set_bool = 1;
7993		set_bool_not = 1;
7994	} else if (branch_opcode == ZEND_JMPZ) {
7995		false_label = target_label;
7996	} else if (branch_opcode == ZEND_JMPNZ) {
7997		true_label = target_label;
7998	} else if (branch_opcode == ZEND_JMPZNZ) {
7999		true_label = target_label2;
8000		false_label = target_label;
8001	} else if (branch_opcode == ZEND_JMPZ_EX) {
8002		set_bool = 1;
8003		false_label = target_label;
8004	} else if (branch_opcode == ZEND_JMPNZ_EX) {
8005		set_bool = 1;
8006		true_label = target_label;
8007	} else {
8008		ZEND_UNREACHABLE();
8009	}
8010
8011	if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
8012		if (zend_is_true(Z_ZV(op1_addr))) {
8013			/* Always TRUE */
8014			if (set_bool) {
8015				if (set_bool_not) {
8016					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8017				} else {
8018					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8019				}
8020			}
8021			if (true_label != (uint32_t)-1) {
8022				|	b =>true_label
8023			}
8024		} else {
8025			/* Always FALSE */
8026			if (set_bool) {
8027				if (set_bool_not) {
8028					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8029				} else {
8030					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8031				}
8032			}
8033			if (false_label != (uint32_t)-1) {
8034				|	b =>false_label
8035			}
8036		}
8037		return 1;
8038	}
8039
8040	if (opline->op1_type == IS_CV && (op1_info & MAY_BE_REF)) {
8041		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
8042		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
8043		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
8044	}
8045
8046	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE)) {
8047		if (!(op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_TRUE))) {
8048			/* Always TRUE */
8049			if (set_bool) {
8050				if (set_bool_not) {
8051					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8052				} else {
8053					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8054				}
8055			}
8056			if (true_label != (uint32_t)-1) {
8057				|	b =>true_label
8058			}
8059		} else {
8060			if (!(op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE)))) {
8061				/* Always FALSE */
8062				if (set_bool) {
8063					if (set_bool_not) {
8064						|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8065					} else {
8066						|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8067					}
8068				}
8069			} else {
8070				|	CMP_ZVAL_TYPE op1_addr, IS_TRUE, ZREG_TMP1
8071				if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) {
8072				    if ((op1_info & MAY_BE_LONG) &&
8073				        !(op1_info & MAY_BE_UNDEF) &&
8074				        !set_bool) {
8075						if (exit_addr) {
8076							if (branch_opcode == ZEND_JMPNZ) {
8077								|	blt >9
8078							} else {
8079								|	blt &exit_addr
8080							}
8081						} else if (false_label != (uint32_t)-1) {
8082							|	blt =>false_label
8083						} else {
8084							|	blt >9
8085						}
8086						jmp_done = 1;
8087					} else {
8088						|	bgt >2
8089					}
8090				}
8091				if (!(op1_info & MAY_BE_TRUE)) {
8092					/* It's FALSE */
8093					if (set_bool) {
8094						if (set_bool_not) {
8095							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8096						} else {
8097							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8098						}
8099					}
8100				} else {
8101					if (exit_addr) {
8102						if (set_bool) {
8103							|	bne >1
8104							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8105							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8106								|	b &exit_addr
8107							} else {
8108								|	b >9
8109							}
8110							|1:
8111							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8112							if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
8113								if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8114									|	bne &exit_addr
8115								}
8116							}
8117						} else {
8118							if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8119								|	beq &exit_addr
8120							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8121								|	bne &exit_addr
8122							} else {
8123								|	beq >9
8124							}
8125						}
8126					} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8127						if (set_bool) {
8128							|	bne >1
8129							|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8130							if (true_label != (uint32_t)-1) {
8131								|	b =>true_label
8132							} else {
8133								|	b >9
8134							}
8135							|1:
8136							|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8137						} else {
8138							if (true_label != (uint32_t)-1) {
8139								|	beq =>true_label
8140							} else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) {
8141								|	bne =>false_label
8142								jmp_done = 1;
8143							} else {
8144								|	beq >9
8145							}
8146						}
8147					} else if (set_bool) {
8148						|	cset REG0w, eq
8149						if (set_bool_not) {
8150							|	neg REG0w, REG0w
8151							|	add REG0w, REG0w, #3
8152						} else {
8153							|	add REG0w, REG0w, #2
8154						}
8155						if ((op1_info & MAY_BE_UNDEF) && (op1_info & MAY_BE_ANY)) {
8156							set_delayed = 1;
8157						} else {
8158							|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
8159						}
8160					}
8161				}
8162			}
8163
8164			/* It's FALSE, but may be UNDEF */
8165			if (op1_info & MAY_BE_UNDEF) {
8166				if (op1_info & MAY_BE_ANY) {
8167					if (set_delayed) {
8168						|	CMP_ZVAL_TYPE op1_addr, IS_UNDEF, ZREG_TMP1
8169						|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
8170						|	beq >1
8171					} else {
8172						|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
8173					}
8174					|.cold_code
8175					|1:
8176				}
8177				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
8178				|	SET_EX_OPLINE opline, REG0
8179				|	EXT_CALL zend_jit_undefined_op_helper, REG0
8180
8181				if (may_throw) {
8182					if (!zend_jit_check_exception_undef_result(Dst, opline)) {
8183						return 0;
8184					}
8185				}
8186
8187				if (exit_addr) {
8188					if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) {
8189						|	b &exit_addr
8190					}
8191				} else if (false_label != (uint32_t)-1) {
8192					|	b =>false_label
8193				}
8194				if (op1_info & MAY_BE_ANY) {
8195					if (exit_addr) {
8196						if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8197							|	b >9
8198						}
8199					} else if (false_label == (uint32_t)-1) {
8200						|	b >9
8201					}
8202					|.code
8203				}
8204			}
8205
8206			if (!jmp_done) {
8207				if (exit_addr) {
8208					if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8209						if (op1_info & MAY_BE_LONG) {
8210							|	b >9
8211						}
8212					} else if (op1_info & MAY_BE_LONG) {
8213						|	b &exit_addr
8214					}
8215				} else if (false_label != (uint32_t)-1) {
8216					|	b =>false_label
8217				} else if ((op1_info & MAY_BE_LONG) || (op1_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
8218					|	b >9
8219				}
8220			}
8221		}
8222	}
8223
8224	if (op1_info & MAY_BE_LONG) {
8225		|2:
8226		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
8227			|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2, ZREG_TMP1
8228		}
8229		if (Z_MODE(op1_addr) == IS_REG) {
8230			|	tst Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr))
8231		} else {
8232			|	LONG_CMP_WITH_CONST op1_addr, Z_L(0), TMP1, TMP2
8233		}
8234		if (set_bool) {
8235			|	cset REG0w, ne
8236			if (set_bool_not) {
8237				|	neg REG0w, REG0w
8238				|	add REG0w, REG0w, #3
8239			} else {
8240				|	add REG0w, REG0w, #2
8241			}
8242			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
8243		}
8244		if (exit_addr) {
8245			if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8246				|	bne &exit_addr
8247			} else {
8248				|	beq &exit_addr
8249			}
8250		} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8251			if (true_label != (uint32_t)-1) {
8252				|	bne =>true_label
8253				if (false_label != (uint32_t)-1) {
8254					|	b =>false_label
8255				}
8256			} else {
8257				|	beq =>false_label
8258			}
8259		}
8260	}
8261
8262	if ((op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) == MAY_BE_DOUBLE) {
8263		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8264			|.cold_code
8265		}
8266	    |2:
8267		|	fmov FPR0, xzr  // TODO: "movi d0, #0" is not recognized by DynASM/arm64
8268		|	DOUBLE_CMP ZREG_FPR0, op1_addr, ZREG_TMP1, ZREG_FPTMP
8269
8270		if (set_bool) {
8271			if (exit_addr) {
8272				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8273					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8274					|	bvs &exit_addr
8275					|	bne &exit_addr
8276					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8277				} else {
8278					|	bvs >1
8279					|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8280					|	beq &exit_addr
8281					|1:
8282					|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8283				}
8284			} else if (false_label != (uint32_t)-1) { // JMPZ_EX
8285				|	bvs >1
8286				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8287				|	beq => false_label
8288				|1:
8289				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8290			} else if (true_label != (uint32_t)-1) { // JMPNZ_EX
8291				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
8292				|	bvs => true_label
8293				|	bne => true_label
8294				|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
8295			} else if (set_bool_not) { // BOOL_NOT
8296				|	mov REG0w, #IS_FALSE
8297				|	bvs >1
8298				|	bne >1
8299				|	mov REG0w, #IS_TRUE
8300				|1:
8301				|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
8302			} else { // BOOL
8303				|	mov REG0w, #IS_TRUE
8304				|	bvs >1
8305				|	bne >1
8306				|	mov REG0w, #IS_FALSE
8307				|1:
8308				|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
8309			}
8310			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8311				|	b >9
8312				|.code
8313			}
8314		} else {
8315			if (exit_addr) {
8316				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8317					|	bvs &exit_addr
8318					|	bne &exit_addr
8319					|1:
8320				} else {
8321					|	bvs >1
8322					|	beq &exit_addr
8323					|1:
8324				}
8325				if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8326					|	b >9
8327				}
8328			} else {
8329				ZEND_ASSERT(true_label != (uint32_t)-1 || false_label != (uint32_t)-1);
8330				if (false_label != (uint32_t)-1 ) {
8331					|	bvs  >1
8332					|	beq  => false_label
8333					|1:
8334					if (true_label != (uint32_t)-1) {
8335						|	b =>true_label
8336					} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8337						|	b >9
8338					}
8339				} else {
8340					|	bvs  => true_label
8341					|	bne  => true_label
8342					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8343						|	b >9
8344					}
8345				}
8346			}
8347			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8348				|.code
8349			}
8350		}
8351	} else if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) {
8352		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8353			|.cold_code
8354			|2:
8355		}
8356		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
8357			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
8358		}
8359		|	SET_EX_OPLINE opline, REG0
8360		|	EXT_CALL zend_is_true, REG0
8361		|	mov REG0, RETVALx
8362
8363		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
8364			(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
8365			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
8366
8367			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
8368				|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >3, ZREG_TMP1, ZREG_TMP2
8369			}
8370			|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
8371			|	GC_DELREF FCARG1x, TMP1w
8372			|	bne >3
8373			// In x86, r0 is used in macro ZVAL_DTOR_FUNC as temporary register, hence, r0 should be saved/restored
8374			// before/after this macro. In AArch64, TMP1 is used, but we still have to store REG0,
8375			// because it's clobbered by function call.
8376			|	str REG0, T1 // save
8377			|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
8378			|	ldr REG0, T1 // restore
8379			|3:
8380		}
8381		if (may_throw) {
8382			|	MEM_LOAD_64_ZTS ldr, REG1, executor_globals, exception, TMP1
8383			|	cbnz REG1, ->exception_handler_undef
8384		}
8385
8386		if (set_bool) {
8387			if (set_bool_not) {
8388				|	neg REG0w, REG0w
8389				|	add REG0w, REG0w, #3
8390			} else {
8391				|	add REG0w, REG0w, #2
8392			}
8393			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
8394			if (exit_addr) {
8395				|	CMP_ZVAL_TYPE res_addr, IS_FALSE, ZREG_TMP1
8396				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8397					|	bne &exit_addr
8398				} else {
8399					|	beq &exit_addr
8400				}
8401			} else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) {
8402				|	CMP_ZVAL_TYPE res_addr, IS_FALSE, ZREG_TMP1
8403				if (true_label != (uint32_t)-1) {
8404					|	bne =>true_label
8405					if (false_label != (uint32_t)-1) {
8406						|	b =>false_label
8407					} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8408						|	b >9
8409					}
8410				} else {
8411					|	beq =>false_label
8412				}
8413			}
8414			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8415				|	b >9
8416				|.code
8417			}
8418		} else {
8419			if (exit_addr) {
8420				if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) {
8421					|	cbnz REG0w, &exit_addr
8422					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8423						|	b >9
8424					}
8425				} else {
8426					|	cbz REG0w, &exit_addr
8427					if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8428						|	b >9
8429					}
8430				}
8431			} else if (true_label != (uint32_t)-1) {
8432				|	cbnz REG0w, =>true_label
8433				if (false_label != (uint32_t)-1) {
8434					|	b =>false_label
8435				} else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8436					|	b >9
8437				}
8438			} else {
8439				|	cbz REG0w, =>false_label
8440				if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8441					|	b >9
8442				}
8443			}
8444
8445			if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) {
8446				|.code
8447			}
8448		}
8449	}
8450
8451	|9:
8452
8453	return 1;
8454}
8455
8456static 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)
8457{
8458	if (op1_addr != op1_def_addr) {
8459		if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
8460			return 0;
8461		}
8462		if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
8463			op1_addr = op1_def_addr;
8464		}
8465	}
8466
8467	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)) {
8468		return 0;
8469	}
8470	if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
8471		return 0;
8472	}
8473	if (op1_info & MAY_BE_UNDEF) {
8474		zend_jit_check_exception(Dst);
8475	}
8476	return 1;
8477}
8478
8479static 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)
8480{
8481	ZEND_ASSERT(opline->op1_type == IS_CV);
8482
8483	if (op2_addr != op2_def_addr) {
8484		if (!zend_jit_update_regs(Dst, opline->op2.var, op2_addr, op2_def_addr, op2_info)) {
8485			return 0;
8486		}
8487		if (Z_MODE(op2_def_addr) == IS_REG && Z_MODE(op2_addr) != IS_REG) {
8488			op2_addr = op2_def_addr;
8489		}
8490	}
8491
8492	if (Z_MODE(op1_addr) != IS_REG
8493	 && Z_MODE(op1_use_addr) == IS_REG
8494	 && !Z_LOAD(op1_use_addr)
8495	 && !Z_STORE(op1_use_addr)) {
8496		/* Force type update */
8497		op1_info |= MAY_BE_UNDEF;
8498	}
8499	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,
8500			may_throw)) {
8501		return 0;
8502	}
8503	if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_addr, op1_def_info, op1_use_addr, op1_info)) {
8504		return 0;
8505	}
8506	if (opline->result_type != IS_UNUSED) {
8507		if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
8508			return 0;
8509		}
8510	}
8511
8512	return 1;
8513}
8514
8515/* copy of hidden zend_closure */
8516typedef struct _zend_closure {
8517	zend_object       std;
8518	zend_function     func;
8519	zval              this_ptr;
8520	zend_class_entry *called_scope;
8521	zif_handler       orig_internal_handler;
8522} zend_closure;
8523
8524static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_t used_stack)
8525{
8526	int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
8527	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8528
8529	if (!exit_addr) {
8530		return 0;
8531	}
8532
8533	|	// Check Stack Overflow
8534	|	MEM_LOAD_64_ZTS ldr, REG1, executor_globals, vm_stack_end, TMP1
8535	|	MEM_LOAD_OP_ZTS sub, ldr, REG1, executor_globals, vm_stack_top, TMP1, TMP2
8536	|	CMP_64_WITH_CONST_32 REG1, used_stack, TMP1
8537	|	blo &exit_addr
8538
8539	return 1;
8540}
8541
8542static 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)
8543{
8544	uint32_t used_stack;
8545	bool stack_check = 1;
8546
8547	// REG0   -> zend_function
8548	// FCARG1 -> used_stack
8549
8550	if (func) {
8551		used_stack = zend_vm_calc_used_stack(opline->extended_value, func);
8552		if ((int)used_stack <= checked_stack) {
8553			stack_check = 0;
8554		}
8555	} else {
8556		used_stack = (ZEND_CALL_FRAME_SLOT + opline->extended_value) * sizeof(zval);
8557
8558		|	// if (EXPECTED(ZEND_USER_CODE(func->type))) {
8559		if (!is_closure) {
8560			|	LOAD_32BIT_VAL FCARG1w, used_stack
8561			|	// Check whether REG0 is an internal function.
8562			|	ldrb TMP1w, [REG0, #offsetof(zend_function, type)]
8563			|	TST_32_WITH_CONST TMP1w, 1, TMP2w
8564			|	bne >1
8565		} else {
8566			|	LOAD_32BIT_VAL FCARG1w, used_stack
8567		}
8568		|	// used_stack += (func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args)) * sizeof(zval);
8569		|	LOAD_32BIT_VAL REG2w, opline->extended_value
8570		if (!is_closure) {
8571			|	ldr TMP1w, [REG0, #offsetof(zend_function, op_array.num_args)]
8572			|	cmp REG2w, TMP1w
8573			|	csel REG2w, REG2w, TMP1w, le
8574			|	ldr TMP1w, [REG0, #offsetof(zend_function, op_array.last_var)]
8575			|	sub REG2w, REG2w, TMP1w
8576			|	ldr TMP1w, [REG0, #offsetof(zend_function, op_array.T)]
8577			|	sub REG2w, REG2w, TMP1w
8578		} else {
8579			|	ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.num_args)]
8580			|	cmp REG2w, TMP1w
8581			|	csel REG2w, REG2w, TMP1w, le
8582			|	ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.last_var)]
8583			|	sub REG2w, REG2w, TMP1w
8584			|	ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.T)]
8585			|	sub REG2w, REG2w, TMP1w
8586		}
8587		|	sxtw REG2, REG2w
8588		|	sub FCARG1x, FCARG1x, REG2, lsl #4
8589		|1:
8590	}
8591
8592	zend_jit_start_reuse_ip();
8593
8594	|	// if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
8595	|	MEM_LOAD_64_ZTS ldr, RX, executor_globals, vm_stack_top, TMP1
8596
8597	if (stack_check) {
8598		|	// Check Stack Overflow
8599		|	MEM_LOAD_64_ZTS ldr, REG2, executor_globals, vm_stack_end, TMP1
8600		|	sub REG2, REG2, RX
8601		if (func) {
8602			|	CMP_64_WITH_CONST_32 REG2, used_stack, TMP1
8603		} else {
8604			|	cmp REG2, FCARG1x
8605		}
8606
8607		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8608			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
8609			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8610
8611			if (!exit_addr) {
8612				return 0;
8613			}
8614
8615			|	blo &exit_addr
8616		} else {
8617			|	blo >1
8618			|	// EG(vm_stack_top) = (zval*)((char*)call + used_stack);
8619			|.cold_code
8620			|1:
8621			if (func) {
8622				|	LOAD_32BIT_VAL FCARG1w, used_stack
8623			}
8624			if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
8625				|	SET_EX_OPLINE opline, REG0
8626				|	EXT_CALL zend_jit_int_extend_stack_helper, REG0
8627			} else {
8628				if (!is_closure) {
8629					|	mov FCARG2x, REG0
8630				} else {
8631					|	add FCARG2x, REG0, #offsetof(zend_closure, func)
8632				}
8633				|	SET_EX_OPLINE opline, REG0
8634				|	EXT_CALL zend_jit_extend_stack_helper, REG0
8635			}
8636			|	mov RX, RETVALx
8637			|	b >1
8638			|.code
8639		}
8640	}
8641
8642	if (func) {
8643		||	if (arm64_may_encode_imm12((int64_t)used_stack)) {
8644		|		MEM_UPDATE_ZTS add, ldr, str, #used_stack, executor_globals, vm_stack_top, REG2, TMP1
8645		||	} else {
8646		|		LOAD_32BIT_VAL TMP1w, used_stack
8647		|		MEM_UPDATE_ZTS add, ldr, str, TMP1, executor_globals, vm_stack_top, REG2, TMP2
8648		||	}
8649	} else {
8650		|	MEM_UPDATE_ZTS add, ldr, str, FCARG1x, executor_globals, vm_stack_top, REG2, TMP1
8651	}
8652	|	// zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
8653	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) {
8654		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
8655		|	LOAD_32BIT_VAL TMP1w, (IS_UNDEF | ZEND_CALL_NESTED_FUNCTION)
8656		|	str TMP1w, EX:RX->This.u1.type_info
8657	}
8658	if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) {
8659		|	// call->func = func;
8660		|1:
8661		|	ADDR_STORE EX:RX->func, func, REG1
8662	} else {
8663		if (!is_closure) {
8664			|	// call->func = func;
8665			|	str REG0, EX:RX->func
8666		} else {
8667			|	// call->func = &closure->func;
8668			|	add REG1, REG0, #offsetof(zend_closure, func)
8669			|	str REG1, EX:RX->func
8670		}
8671		|1:
8672	}
8673	if (opline->opcode == ZEND_INIT_METHOD_CALL) {
8674		|	// Z_PTR(call->This) = obj;
8675		|	ldr REG1, T1
8676		|	str REG1, EX:RX->This.value.ptr
8677	    if (opline->op1_type == IS_UNUSED || delayed_fetch_this) {
8678			|	// call->call_info |= ZEND_CALL_HAS_THIS;
8679			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8680				|	LOAD_32BIT_VAL TMP1w, ZEND_CALL_HAS_THIS
8681				|	str TMP1w, EX:RX->This.u1.type_info
8682			} else {
8683				|	ldr TMP1w, EX:RX->This.u1.type_info
8684				|	BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_HAS_THIS, TMP2w
8685				|	str TMP1w, EX:RX->This.u1.type_info
8686			}
8687	    } else {
8688			if (opline->op1_type == IS_CV) {
8689				|	// GC_ADDREF(obj);
8690				|	GC_ADDREF REG1, TMP1w
8691			}
8692			|	// call->call_info |= ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS;
8693			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8694				|	LOAD_32BIT_VAL TMP1w, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS)
8695				|	str TMP1w, EX:RX->This.u1.type_info
8696			} else {
8697				|	ldr TMP1w, EX:RX->This.u1.type_info
8698				|	BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS), TMP2w
8699				|	str TMP1w, EX:RX->This.u1.type_info
8700			}
8701	    }
8702	} else if (!is_closure) {
8703		|	// Z_CE(call->This) = called_scope;
8704		|	str xzr, EX:RX->This.value.ptr
8705	} else {
8706		if (opline->op2_type == IS_CV) {
8707			|	// GC_ADDREF(closure);
8708			|	GC_ADDREF REG0, TMP1w
8709		}
8710		|	//	object_or_called_scope = closure->called_scope;
8711		|	ldr REG1, [REG0, #offsetof(zend_closure, called_scope)]
8712		|	str REG1, EX:RX->This.value.ptr
8713		|	// call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE |
8714		|	//	(closure->func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE);
8715		|	ldr REG2w, [REG0, #offsetof(zend_closure, func.common.fn_flags)]
8716		|	BW_OP_32_WITH_CONST and, REG2w, REG2w, ZEND_ACC_FAKE_CLOSURE, TMP1w
8717		|	BW_OP_32_WITH_CONST orr, REG2w, REG2w, (ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE), TMP1w
8718		|	//	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
8719		|	ldrb TMP1w, [REG0, #offsetof(zend_closure, this_ptr.u1.v.type)]
8720		|	cmp TMP1w, #IS_UNDEF
8721		|	beq >1
8722		|	//	call_info |= ZEND_CALL_HAS_THIS;
8723		|	BW_OP_32_WITH_CONST orr, REG2w, REG2w, ZEND_CALL_HAS_THIS, TMP1w
8724		|	//	object_or_called_scope = Z_OBJ(closure->this_ptr);
8725		|	ldr REG1, [REG0, #offsetof(zend_closure, this_ptr.value.ptr)]
8726	    |1:
8727		|	// ZEND_SET_CALL_INFO(call, 0, call_info);
8728		|	ldr TMP1w, EX:RX->This.u1.type_info
8729		|	orr TMP1w, TMP1w, REG2w
8730		|	str TMP1w, EX:RX->This.u1.type_info
8731		|	// Z_PTR(call->This) = object_or_called_scope;
8732		|	str REG1, EX:RX->This.value.ptr
8733		|	ldr TMP1, [REG0, #offsetof(zend_closure, func.op_array.run_time_cache__ptr)]
8734		|	cbnz TMP1, >1
8735		|	add FCARG1x, REG0, #offsetof(zend_closure, func)
8736		|	EXT_CALL zend_jit_init_func_run_time_cache_helper, REG0
8737		|1:
8738	}
8739	|	// ZEND_CALL_NUM_ARGS(call) = num_args;
8740	|	LOAD_32BIT_VAL TMP1w, opline->extended_value
8741	|	str TMP1w, EX:RX->This.u2.num_args
8742
8743	return 1;
8744}
8745
8746static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zend_function *func, const zend_op *to_opline)
8747{
8748	int32_t exit_point;
8749	const void *exit_addr;
8750
8751	if (func->type == ZEND_INTERNAL_FUNCTION) {
8752#ifdef ZEND_WIN32
8753		// TODO: ASLR may cause different addresses in different workers ???
8754		return 0;
8755#endif
8756	} else if (func->type == ZEND_USER_FUNCTION) {
8757		if (!zend_accel_in_shm(func->op_array.opcodes)) {
8758			/* op_array and op_array->opcodes are not persistent. We can't link. */
8759			return 0;
8760		}
8761	} else {
8762		ZEND_UNREACHABLE();
8763		return 0;
8764	}
8765
8766	exit_point = zend_jit_trace_get_exit_point(to_opline, ZEND_JIT_EXIT_POLYMORPHISM);
8767	exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8768	if (!exit_addr) {
8769		return 0;
8770	}
8771
8772	|	// call = EX(call);
8773	|	ldr REG1, EX->call
8774	while (level > 0) {
8775		|	ldr REG1, EX:REG1->prev_execute_data
8776		level--;
8777	}
8778
8779	if (func->type == ZEND_USER_FUNCTION &&
8780	    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
8781	     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
8782	     !func->common.function_name)) {
8783		const zend_op *opcodes = func->op_array.opcodes;
8784
8785		|	ldr REG1, EX:REG1->func
8786		|	LOAD_ADDR REG2, ((ptrdiff_t)opcodes)
8787		|	ldr TMP1, [REG1, #offsetof(zend_op_array, opcodes)]
8788		|	cmp TMP1, REG2
8789		|	bne &exit_addr
8790	} else {
8791		|	LOAD_ADDR REG2, ((ptrdiff_t)func)
8792		|	ldr TMP1, EX:REG1->func
8793		|	cmp TMP1, REG2
8794		|	bne &exit_addr
8795	}
8796
8797	return 1;
8798}
8799
8800static 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)
8801{
8802	zend_func_info *info = ZEND_FUNC_INFO(op_array);
8803	zend_call_info *call_info = NULL;
8804	zend_function *func = NULL;
8805
8806	if (delayed_call_chain) {
8807		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
8808			return 0;
8809		}
8810	}
8811
8812	if (info) {
8813		call_info = info->callee_info;
8814		while (call_info && call_info->caller_init_opline != opline) {
8815			call_info = call_info->next_callee;
8816		}
8817		if (call_info && call_info->callee_func && !call_info->is_prototype) {
8818			func = call_info->callee_func;
8819		}
8820	}
8821
8822	if (!func
8823	 && trace
8824	 && trace->op == ZEND_JIT_TRACE_INIT_CALL) {
8825		func = (zend_function*)trace->func;
8826	}
8827
8828	if (opline->opcode == ZEND_INIT_FCALL
8829	 && func
8830	 && func->type == ZEND_INTERNAL_FUNCTION) {
8831		/* load constant address later */
8832	} else if (func && op_array == &func->op_array) {
8833		/* recursive call */
8834		|	ldr REG0, EX->func
8835	} else {
8836		|	// if (CACHED_PTR(opline->result.num))
8837		|	ldr REG2, EX->run_time_cache
8838		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG2, opline->result.num, TMP1
8839		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
8840		 && func
8841		 && (func->common.fn_flags & ZEND_ACC_IMMUTABLE)
8842		 && opline->opcode != ZEND_INIT_FCALL) {
8843			/* Called func may be changed because of recompilation. See ext/opcache/tests/jit/init_fcall_003.phpt */
8844			|	LOAD_ADDR REG1, ((ptrdiff_t)func)
8845			|	cmp REG0, REG1
8846			|	bne >1
8847		} else {
8848			|	cbz REG0, >1
8849		}
8850		|.cold_code
8851		|1:
8852		if (opline->opcode == ZEND_INIT_FCALL
8853		 && func
8854		 && func->type == ZEND_USER_FUNCTION
8855		 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) {
8856			|	LOAD_ADDR FCARG1x, func
8857			|	MEM_ACCESS_64_WITH_UOFFSET str, FCARG1x, REG2, opline->result.num, TMP1
8858			|	EXT_CALL zend_jit_init_func_run_time_cache_helper, REG0
8859			|	mov REG0, RETVALx
8860			|	b >3
8861		} else {
8862			zval *zv = RT_CONSTANT(opline, opline->op2);
8863
8864			if (opline->opcode == ZEND_INIT_FCALL) {
8865				|	LOAD_ADDR FCARG1x, Z_STR_P(zv);
8866				|	ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1
8867				|	EXT_CALL zend_jit_find_func_helper, REG0
8868			} else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) {
8869				|	LOAD_ADDR FCARG1x, Z_STR_P(zv + 1);
8870				|	ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1
8871				|	EXT_CALL zend_jit_find_func_helper, REG0
8872			} else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
8873				|	LOAD_ADDR FCARG1x, zv;
8874				|	ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1
8875				|	EXT_CALL zend_jit_find_ns_func_helper, REG0
8876			} else {
8877				ZEND_UNREACHABLE();
8878			}
8879			|	// Get the return value of function zend_jit_find_func_helper/zend_jit_find_ns_func_helper
8880			|	mov REG0, RETVALx
8881			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8882				int32_t exit_point = zend_jit_trace_get_exit_point(opline,
8883					func && (func->common.fn_flags & ZEND_ACC_IMMUTABLE) ? ZEND_JIT_EXIT_INVALIDATE : 0);
8884				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8885
8886				if (!exit_addr) {
8887					return 0;
8888				}
8889
8890				if (!func || opline->opcode == ZEND_INIT_FCALL) {
8891					|	cbnz REG0, >3
8892				} else if (func->type == ZEND_USER_FUNCTION
8893					 && !(func->common.fn_flags & ZEND_ACC_IMMUTABLE)) {
8894					const zend_op *opcodes = func->op_array.opcodes;
8895
8896					|	LOAD_ADDR REG1, ((ptrdiff_t)opcodes)
8897					|	ldr TMP1, [REG0, #offsetof(zend_op_array, opcodes)]
8898					|	cmp TMP1, REG1
8899					|	beq >3
8900				} else {
8901					|	LOAD_ADDR REG1, ((ptrdiff_t)func)
8902					|	cmp REG0, REG1
8903					|	beq >3
8904				}
8905				|	b &exit_addr
8906			} else {
8907				|	cbnz REG0, >3
8908				|	// SAVE_OPLINE();
8909				|	SET_EX_OPLINE opline, REG0
8910				|	b ->undefined_function
8911			}
8912		}
8913		|.code
8914		|3:
8915	}
8916
8917	if (!zend_jit_push_call_frame(Dst, opline, op_array, func, 0, 0, checked_stack)) {
8918		return 0;
8919	}
8920
8921	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
8922		if (!zend_jit_save_call_chain(Dst, call_level)) {
8923			return 0;
8924		}
8925	} else {
8926		delayed_call_chain = 1;
8927		delayed_call_level = call_level;
8928	}
8929
8930	return 1;
8931}
8932
8933static int zend_jit_init_method_call(dasm_State          **Dst,
8934                                     const zend_op        *opline,
8935                                     uint32_t              b,
8936                                     const zend_op_array  *op_array,
8937                                     zend_ssa             *ssa,
8938                                     const zend_ssa_op    *ssa_op,
8939                                     int                   call_level,
8940                                     uint32_t              op1_info,
8941                                     zend_jit_addr         op1_addr,
8942                                     zend_class_entry     *ce,
8943                                     bool                  ce_is_instanceof,
8944                                     bool                  on_this,
8945                                     bool                  delayed_fetch_this,
8946                                     zend_class_entry     *trace_ce,
8947                                     zend_jit_trace_rec   *trace,
8948                                     int                   checked_stack,
8949                                     bool                  polymorphic_side_trace)
8950{
8951	zend_func_info *info = ZEND_FUNC_INFO(op_array);
8952	zend_call_info *call_info = NULL;
8953	zend_function *func = NULL;
8954	zval *function_name;
8955
8956	ZEND_ASSERT(opline->op2_type == IS_CONST);
8957	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
8958
8959	function_name = RT_CONSTANT(opline, opline->op2);
8960
8961	if (info) {
8962		call_info = info->callee_info;
8963		while (call_info && call_info->caller_init_opline != opline) {
8964			call_info = call_info->next_callee;
8965		}
8966		if (call_info && call_info->callee_func && !call_info->is_prototype) {
8967			func = call_info->callee_func;
8968		}
8969	}
8970
8971	if (polymorphic_side_trace) {
8972		/* function is passed in r0 from parent_trace */
8973	} else {
8974		if (on_this) {
8975			zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
8976
8977			|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
8978		} else {
8979		    if (op1_info & MAY_BE_REF) {
8980				if (opline->op1_type == IS_CV) {
8981					if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
8982						|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
8983					}
8984					|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
8985					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
8986				} else {
8987					/* Hack: Convert reference to regular value to simplify JIT code */
8988					ZEND_ASSERT(Z_REG(op1_addr) == ZREG_FP);
8989					|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1
8990					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
8991					|	EXT_CALL zend_jit_unref_helper, REG0
8992					|1:
8993				}
8994			}
8995			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
8996				if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
8997					int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
8998					const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
8999
9000					if (!exit_addr) {
9001						return 0;
9002					}
9003					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
9004				} else {
9005					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
9006					|.cold_code
9007					|1:
9008					if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
9009						|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
9010					}
9011					|	SET_EX_OPLINE opline, REG0
9012					if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9013						|	EXT_CALL zend_jit_invalid_method_call_tmp, REG0
9014					} else {
9015						|	EXT_CALL zend_jit_invalid_method_call, REG0
9016					}
9017					|	b ->exception_handler
9018					|.code
9019				}
9020			}
9021			|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
9022		}
9023
9024		if (delayed_call_chain) {
9025			if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9026				return 0;
9027			}
9028		}
9029
9030		|	str FCARG1x, T1 // save
9031
9032		if (func) {
9033			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
9034			|	ldr REG0, EX->run_time_cache
9035			|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->result.num + sizeof(void*)), TMP1
9036			|	cbz REG0, >1
9037		} else {
9038			|	// if (CACHED_PTR(opline->result.num) == obj->ce)) {
9039			|	ldr REG0, EX->run_time_cache
9040			|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, opline->result.num, TMP1
9041			|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
9042			|	cmp REG2, TMP1
9043			|	bne >1
9044			|	// fbc = CACHED_PTR(opline->result.num + sizeof(void*));
9045			|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->result.num + sizeof(void*)), TMP1
9046		}
9047
9048		|.cold_code
9049		|1:
9050		|	LOAD_ADDR FCARG2x, function_name
9051		if (TMP_ZVAL_OFFSET == 0) {
9052			|	mov CARG3, sp
9053		} else {
9054			|	add CARG3, sp, #TMP_ZVAL_OFFSET
9055		}
9056		|	SET_EX_OPLINE opline, REG0
9057		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9058			|	EXT_CALL zend_jit_find_method_tmp_helper, REG0
9059		} else {
9060			|	EXT_CALL zend_jit_find_method_helper, REG0
9061		}
9062		|	mov REG0, RETVALx
9063		|	cbnz REG0, >2
9064		|	b ->exception_handler
9065		|.code
9066		|2:
9067	}
9068
9069	if ((!func || zend_jit_may_be_modified(func, op_array))
9070	 && trace
9071	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
9072	 && trace->func
9073	) {
9074		int32_t exit_point;
9075		const void *exit_addr;
9076
9077		exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : ZEND_JIT_EXIT_METHOD_CALL);
9078		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9079		if (!exit_addr) {
9080			return 0;
9081		}
9082
9083		func = (zend_function*)trace->func;
9084
9085		if (func->type == ZEND_USER_FUNCTION &&
9086		    (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) ||
9087		     (func->common.fn_flags & ZEND_ACC_CLOSURE) ||
9088		     !func->common.function_name)) {
9089			const zend_op *opcodes = func->op_array.opcodes;
9090
9091			|	LOAD_ADDR TMP1, opcodes
9092			|	ldr TMP2, [REG0, #offsetof(zend_op_array, opcodes)]
9093			|	cmp TMP2, TMP1
9094			|	bne &exit_addr
9095		} else {
9096			|	LOAD_ADDR TMP1, func
9097			|	cmp REG0, TMP1
9098			|	bne &exit_addr
9099		}
9100	}
9101
9102	if (!func) {
9103		|	// if (fbc->common.fn_flags & ZEND_ACC_STATIC) {
9104		|	ldr TMP1w, [REG0, #offsetof(zend_function, common.fn_flags)]
9105		|	TST_32_WITH_CONST TMP1w, ZEND_ACC_STATIC, TMP2w
9106		|	bne >1
9107		|.cold_code
9108		|1:
9109	}
9110
9111	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) != 0) {
9112		|	ldr FCARG1x, T1 // restore
9113		|	mov FCARG2x, REG0
9114		|	LOAD_32BIT_VAL CARG3w, opline->extended_value
9115		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) {
9116			|	EXT_CALL zend_jit_push_static_metod_call_frame_tmp, REG0
9117		} else {
9118			|	EXT_CALL zend_jit_push_static_metod_call_frame, REG0
9119		}
9120		if ((opline->op1_type & (IS_VAR|IS_TMP_VAR) && !delayed_fetch_this)) {
9121			|	cbz RETVALx, ->exception_handler
9122		}
9123		|	mov RX, RETVALx
9124	}
9125
9126	if (!func) {
9127		|	b >9
9128		|.code
9129	}
9130
9131	if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) == 0) {
9132		if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 0, delayed_fetch_this, checked_stack)) {
9133			return 0;
9134		}
9135	}
9136
9137	if (!func) {
9138		|9:
9139	}
9140	zend_jit_start_reuse_ip();
9141
9142	if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9143		if (!zend_jit_save_call_chain(Dst, call_level)) {
9144			return 0;
9145		}
9146	} else {
9147		delayed_call_chain = 1;
9148		delayed_call_level = call_level;
9149	}
9150
9151	return 1;
9152}
9153
9154static int zend_jit_init_closure_call(dasm_State          **Dst,
9155                                      const zend_op        *opline,
9156                                      uint32_t              b,
9157                                      const zend_op_array  *op_array,
9158                                      zend_ssa             *ssa,
9159                                      const zend_ssa_op    *ssa_op,
9160                                      int                   call_level,
9161                                      zend_jit_trace_rec   *trace,
9162                                      int                   checked_stack)
9163{
9164	zend_function *func = NULL;
9165	zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
9166
9167	|	GET_ZVAL_PTR REG0, op2_addr, TMP1
9168
9169	if (ssa->var_info[ssa_op->op2_use].ce != zend_ce_closure
9170	 && !(ssa->var_info[ssa_op->op2_use].type & MAY_BE_CLASS_GUARD)) {
9171		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9172		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9173
9174		if (!exit_addr) {
9175			return 0;
9176		}
9177
9178		|	LOAD_ADDR FCARG1x, ((ptrdiff_t)zend_ce_closure)
9179		|	ldr, TMP1, [REG0, #offsetof(zend_object, ce)]
9180		|	cmp TMP1, FCARG1x
9181		|	bne &exit_addr
9182		if (ssa->var_info && ssa_op->op2_use >= 0) {
9183			ssa->var_info[ssa_op->op2_use].type |= MAY_BE_CLASS_GUARD;
9184			ssa->var_info[ssa_op->op2_use].ce = zend_ce_closure;
9185			ssa->var_info[ssa_op->op2_use].is_instanceof = 0;
9186		}
9187	}
9188
9189	if (trace
9190	 && trace->op == ZEND_JIT_TRACE_INIT_CALL
9191	 && trace->func
9192	 && trace->func->type == ZEND_USER_FUNCTION) {
9193		const zend_op *opcodes;
9194		int32_t exit_point;
9195		const void *exit_addr;
9196
9197		func = (zend_function*)trace->func;
9198		opcodes = func->op_array.opcodes;
9199		exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_CLOSURE_CALL);
9200		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9201		if (!exit_addr) {
9202			return 0;
9203		}
9204
9205		|	LOAD_ADDR FCARG1x, ((ptrdiff_t)opcodes)
9206		|	ldr TMP1, [REG0, #offsetof(zend_closure, func.op_array.opcodes)]
9207		|	cmp TMP1, FCARG1x
9208		|	bne &exit_addr
9209	}
9210
9211	if (delayed_call_chain) {
9212		if (!zend_jit_save_call_chain(Dst, delayed_call_level)) {
9213			return 0;
9214		}
9215	}
9216
9217	if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 1, 0, checked_stack)) {
9218		return 0;
9219	}
9220
9221	if (zend_jit_needs_call_chain(NULL, b, op_array, ssa, ssa_op, opline, call_level, trace)) {
9222		if (!zend_jit_save_call_chain(Dst, call_level)) {
9223			return 0;
9224		}
9225	} else {
9226		delayed_call_chain = 1;
9227		delayed_call_level = call_level;
9228	}
9229
9230	if (trace
9231	 && trace->op == ZEND_JIT_TRACE_END
9232	 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
9233		if (!zend_jit_set_valid_ip(Dst, opline + 1)) {
9234			return 0;
9235		}
9236	}
9237
9238	return 1;
9239}
9240
9241static 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)
9242{
9243	zend_func_info *info = ZEND_FUNC_INFO(op_array);
9244	zend_call_info *call_info = NULL;
9245	const zend_function *func = NULL;
9246	uint32_t i;
9247	zend_jit_addr res_addr;
9248	uint32_t call_num_args = 0;
9249	bool unknown_num_args = 0;
9250	const void *exit_addr = NULL;
9251	const zend_op *prev_opline;
9252
9253	if (RETURN_VALUE_USED(opline)) {
9254		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
9255	} else {
9256		/* CPU stack allocated temporary zval */
9257		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RSP, TMP_ZVAL_OFFSET);
9258	}
9259
9260	prev_opline = opline - 1;
9261	while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) {
9262		prev_opline--;
9263	}
9264	if (prev_opline->opcode == ZEND_SEND_UNPACK || prev_opline->opcode == ZEND_SEND_ARRAY ||
9265			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
9266		unknown_num_args = 1;
9267	}
9268
9269	if (info) {
9270		call_info = info->callee_info;
9271		while (call_info && call_info->caller_call_opline != opline) {
9272			call_info = call_info->next_callee;
9273		}
9274		if (call_info && call_info->callee_func && !call_info->is_prototype) {
9275			func = call_info->callee_func;
9276		}
9277		if ((op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)
9278		 && JIT_G(current_frame)
9279		 && JIT_G(current_frame)->call
9280		 && !JIT_G(current_frame)->call->func) {
9281			call_info = NULL; func = NULL; /* megamorphic call from trait */
9282		}
9283	}
9284	if (!func) {
9285		/* resolve function at run time */
9286	} else if (func->type == ZEND_USER_FUNCTION) {
9287		ZEND_ASSERT(opline->opcode != ZEND_DO_ICALL);
9288		call_num_args = call_info->num_args;
9289	} else if (func->type == ZEND_INTERNAL_FUNCTION) {
9290		ZEND_ASSERT(opline->opcode != ZEND_DO_UCALL);
9291		call_num_args = call_info->num_args;
9292	} else {
9293		ZEND_UNREACHABLE();
9294	}
9295
9296	if (trace && !func) {
9297		if (trace->op == ZEND_JIT_TRACE_DO_ICALL) {
9298			ZEND_ASSERT(trace->func->type == ZEND_INTERNAL_FUNCTION);
9299#ifndef ZEND_WIN32
9300			// TODO: ASLR may cause different addresses in different workers ???
9301			func = trace->func;
9302			if (JIT_G(current_frame) &&
9303			    JIT_G(current_frame)->call &&
9304			    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
9305				call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
9306			} else {
9307				unknown_num_args = 1;
9308			}
9309#endif
9310		} else if (trace->op == ZEND_JIT_TRACE_ENTER) {
9311			ZEND_ASSERT(trace->func->type == ZEND_USER_FUNCTION);
9312			if (zend_accel_in_shm(trace->func->op_array.opcodes)) {
9313				func = trace->func;
9314				if (JIT_G(current_frame) &&
9315				    JIT_G(current_frame)->call &&
9316				    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) {
9317					call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call);
9318				} else {
9319					unknown_num_args = 1;
9320				}
9321			}
9322		}
9323	}
9324
9325	bool may_have_extra_named_params =
9326		opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS &&
9327		(!func || func->common.fn_flags & ZEND_ACC_VARIADIC);
9328
9329	if (!reuse_ip) {
9330		zend_jit_start_reuse_ip();
9331		|	// call = EX(call);
9332		|	ldr RX, EX->call
9333	}
9334	zend_jit_stop_reuse_ip();
9335
9336	|	// fbc = call->func;
9337	|	// mov r2, EX:RX->func ???
9338	|	// SAVE_OPLINE();
9339	|	SET_EX_OPLINE opline, REG0
9340
9341	if (opline->opcode == ZEND_DO_FCALL) {
9342		if (!func) {
9343			if (trace) {
9344				uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9345
9346				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9347				if (!exit_addr) {
9348					return 0;
9349				}
9350				|	ldr REG0, EX:RX->func
9351				|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
9352				|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
9353				|	bne &exit_addr
9354			}
9355		}
9356	}
9357
9358	if (!delayed_call_chain) {
9359		if (call_level == 1) {
9360			|	str xzr, EX->call
9361		} else {
9362			|	//EX(call) = call->prev_execute_data;
9363			|	ldr REG0, EX:RX->prev_execute_data
9364			|	str REG0, EX->call
9365		}
9366	}
9367	delayed_call_chain = 0;
9368
9369	|	//call->prev_execute_data = execute_data;
9370	|	str EX, EX:RX->prev_execute_data
9371
9372	if (!func) {
9373		|	ldr REG0, EX:RX->func
9374	}
9375
9376	if (opline->opcode == ZEND_DO_FCALL) {
9377		if (!func) {
9378			if (!trace) {
9379				|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
9380				|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
9381				|	bne >1
9382				|.cold_code
9383				|1:
9384				if (!GCC_GLOBAL_REGS) {
9385					|	mov FCARG1x, RX
9386				}
9387				|	EXT_CALL zend_jit_deprecated_helper, REG0
9388				|	GET_LOW_8BITS RETVALw, RETVALw
9389				|	ldr REG0, EX:RX->func // reload
9390				|	cbnz RETVALw, >1      // Result is 0 on exception
9391				|	b ->exception_handler
9392				|.code
9393				|1:
9394			}
9395		} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
9396			if (!GCC_GLOBAL_REGS) {
9397				|	mov FCARG1x, RX
9398			}
9399			|	EXT_CALL zend_jit_deprecated_helper, REG0
9400			|	cbz RETVALw, ->exception_handler
9401		}
9402	}
9403
9404	if (!func
9405	 && opline->opcode != ZEND_DO_UCALL
9406	 && opline->opcode != ZEND_DO_ICALL) {
9407		|	ldrb TMP1w, [REG0, #offsetof(zend_function, type)]
9408		|	cmp TMP1w, #ZEND_USER_FUNCTION
9409		|	bne >8
9410	}
9411
9412	if ((!func || func->type == ZEND_USER_FUNCTION)
9413	 && opline->opcode != ZEND_DO_ICALL) {
9414		|	// EX(call) = NULL;
9415		|	str xzr, EX:RX->call
9416
9417		if (RETURN_VALUE_USED(opline)) {
9418			|	// EX(return_value) = EX_VAR(opline->result.var);
9419			|	LOAD_ZVAL_ADDR REG2, res_addr
9420			|	str REG2, EX:RX->return_value
9421		} else {
9422			|	// EX(return_value) = 0;
9423			|	str xzr, EX:RX->return_value
9424		}
9425
9426		//EX_LOAD_RUN_TIME_CACHE(op_array);
9427		if (!func || func->op_array.cache_size) {
9428			if (func && op_array == &func->op_array) {
9429				/* recursive call */
9430				if (trace || func->op_array.cache_size > sizeof(void*)) {
9431					|	ldr REG2, EX->run_time_cache
9432					|	str REG2, EX:RX->run_time_cache
9433				}
9434			} else {
9435// Always defined as ZEND_MAP_PTR_KIND_PTR_OR_OFFSET. See Zend/zend_map_ptr.h.
9436#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR
9437				if (func) {
9438					|	ldr REG0, EX:RX->func
9439				}
9440				|	ldr REG2, [REG0, #offsetof(zend_op_array, run_time_cache__ptr)]
9441				|	ldr REG2, [REG2]
9442#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET
9443				if (func && !(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
9444					if (ZEND_MAP_PTR_IS_OFFSET(func->op_array.run_time_cache)) {
9445						|	MEM_LOAD_64_ZTS ldr, REG2, compiler_globals, map_ptr_base, TMP1
9446						|	ADD_SUB_64_WITH_CONST add, REG2, REG2, (uintptr_t)ZEND_MAP_PTR(func->op_array.run_time_cache), TMP1
9447						|	ldr REG2, [REG2]
9448					} else if ((func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)
9449					        && (!func->op_array.scope || (func->op_array.scope->ce_flags & ZEND_ACC_LINKED))) {
9450						if (func) {
9451							|	ldr REG0, EX:RX->func
9452						}
9453						|	ldr REG2, [REG0, #offsetof(zend_op_array, run_time_cache__ptr)]
9454						|	MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1
9455						|	ldr REG2, [REG2]
9456					} else {
9457						/* the called op_array may be not persisted yet */
9458						if (func) {
9459							|	ldr REG0, EX:RX->func
9460						}
9461						|	ldr REG2, [REG0, #offsetof(zend_op_array, run_time_cache__ptr)]
9462						|	TST_64_WITH_ONE REG2
9463						|	beq >1
9464						|	MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1
9465						|1:
9466						|	ldr REG2, [REG2]
9467					}
9468				} else {
9469					if (func) {
9470						|	ldr REG0, EX:RX->func
9471					}
9472					|	ldr REG2, [REG0, #offsetof(zend_op_array, run_time_cache__ptr)]
9473					|	TST_64_WITH_ONE REG2
9474					|	beq >1
9475					|	MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1
9476					|1:
9477					|	ldr REG2, [REG2]
9478				}
9479#else
9480# error "Unknown ZEND_MAP_PTR_KIND"
9481#endif
9482				|	str REG2, EX:RX->run_time_cache
9483			}
9484		}
9485
9486		|	// EG(current_execute_data) = execute_data;
9487		|	MEM_STORE_64_ZTS str, RX, executor_globals, current_execute_data, REG1
9488		|	mov FP, RX
9489
9490		|	// opline = op_array->opcodes;
9491		if (func && !unknown_num_args) {
9492			|	ADD_SUB_64_WITH_CONST_32 add, TMP1, RX, (EX_NUM_TO_VAR(call_num_args) + offsetof(zval, u1.type_info)), TMP1 // induction variable
9493			for (i = call_num_args; i < func->op_array.last_var; i++) {
9494				|	// ZVAL_UNDEF(EX_VAR(n))
9495				|	str wzr, [TMP1], #16
9496			}
9497
9498			if (call_num_args <= func->op_array.num_args) {
9499				if (!trace || (trace->op == ZEND_JIT_TRACE_END
9500				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
9501					uint32_t num_args;
9502
9503					if ((func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
9504						if (trace) {
9505							num_args = 0;
9506						} else if (call_info) {
9507							num_args = skip_valid_arguments(op_array, ssa, call_info);
9508						} else {
9509							num_args = call_num_args;
9510						}
9511					} else {
9512						num_args = call_num_args;
9513					}
9514					if (zend_accel_in_shm(func->op_array.opcodes)) {
9515						|	LOAD_IP_ADDR (func->op_array.opcodes + num_args)
9516					} else {
9517						|	ldr REG0, EX->func
9518						||	ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(num_args * sizeof(zend_op))));
9519						if (GCC_GLOBAL_REGS) {
9520							|	ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
9521							if (num_args) {
9522								|	add IP, IP, #(num_args * sizeof(zend_op))
9523							}
9524						} else {
9525							|	ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)]
9526							if (num_args) {
9527								|	add FCARG1x, FCARG1x, #(num_args * sizeof(zend_op))
9528							}
9529							|	str FCARG1x, EX->opline
9530						}
9531					}
9532
9533					if (GCC_GLOBAL_REGS && !trace && op_array == &func->op_array
9534							&& num_args >= op_array->required_num_args) {
9535						/* recursive call */
9536						if (ZEND_OBSERVER_ENABLED) {
9537							|	SAVE_IP
9538							|	mov FCARG1x, FP
9539							|	EXT_CALL zend_observer_fcall_begin, REG0
9540						}
9541#ifdef CONTEXT_THREADED_JIT
9542						|	NIY	// TODO
9543#else
9544						|	b =>num_args
9545#endif
9546						return 1;
9547					}
9548				}
9549			} else {
9550				if (!trace || (trace->op == ZEND_JIT_TRACE_END
9551				 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
9552					if (func && zend_accel_in_shm(func->op_array.opcodes)) {
9553						|	LOAD_IP_ADDR (func->op_array.opcodes)
9554					} else if (GCC_GLOBAL_REGS) {
9555						|	ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
9556					} else {
9557						|	ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)]
9558						|	str FCARG1x, EX->opline
9559					}
9560				}
9561				if (!GCC_GLOBAL_REGS) {
9562					|	mov FCARG1x, FP
9563				}
9564				|	EXT_CALL zend_jit_copy_extra_args_helper, REG0
9565			}
9566		} else {
9567			|	// opline = op_array->opcodes
9568			if (func && zend_accel_in_shm(func->op_array.opcodes)) {
9569				|	LOAD_IP_ADDR (func->op_array.opcodes)
9570			} else if (GCC_GLOBAL_REGS) {
9571				|	ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
9572			} else {
9573				|	ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)]
9574				|	str FCARG1x, EX->opline
9575			}
9576			if (func) {
9577				|	// num_args = EX_NUM_ARGS();
9578				|	ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)]
9579				|	// if (UNEXPECTED(num_args > first_extra_arg))
9580				|	CMP_32_WITH_CONST REG1w, (func->op_array.num_args), TMP1w
9581			} else {
9582				|	// first_extra_arg = op_array->num_args;
9583				|	ldr REG2w, [REG0, #offsetof(zend_op_array, num_args)]
9584				|	// num_args = EX_NUM_ARGS();
9585				|	ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)]
9586				|	// if (UNEXPECTED(num_args > first_extra_arg))
9587				|	cmp REG1w, REG2w
9588			}
9589			|	bgt >1
9590			|.cold_code
9591			|1:
9592			if (!GCC_GLOBAL_REGS) {
9593				|	mov FCARG1x, FP
9594			}
9595			|	EXT_CALL zend_jit_copy_extra_args_helper, REG0
9596			if (!func) {
9597				|	ldr REG0, EX->func // reload
9598			}
9599			|	ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)] // reload
9600			|	b >1
9601			|.code
9602			if (!func || (func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0) {
9603				if (!func) {
9604					|	// if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0))
9605					|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
9606					|	TST_32_WITH_CONST TMP1w, ZEND_ACC_HAS_TYPE_HINTS, TMP2w
9607					|	bne >1
9608				}
9609				|	// opline += num_args;
9610				||	ZEND_ASSERT(sizeof(zend_op) == 32);
9611				|	mov REG2w, REG1w
9612				|	ADD_IP_SHIFT REG2, lsl #5, TMP1
9613			}
9614			|1:
9615			|	// if (EXPECTED((int)num_args < op_array->last_var)) {
9616			if (func) {
9617				|	LOAD_32BIT_VAL REG2w, func->op_array.last_var
9618			} else {
9619				|	ldr REG2w, [REG0, #offsetof(zend_op_array, last_var)]
9620			}
9621			|	subs REG2w, REG2w, REG1w
9622			|	ble >3
9623			|	// zval *var = EX_VAR_NUM(num_args);
9624			|	add REG1, FP, REG1, lsl #4
9625			||	ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(ZEND_CALL_FRAME_SLOT * sizeof(zval))));
9626			|	add REG1, REG1, #(ZEND_CALL_FRAME_SLOT * sizeof(zval))
9627			|2:
9628			|	SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w
9629			|	add REG1, REG1, #16
9630			|	subs REG2w, REG2w, #1
9631			|	bne <2
9632			|3:
9633		}
9634
9635		if (ZEND_OBSERVER_ENABLED) {
9636			|	SAVE_IP
9637			|	mov FCARG1x, FP
9638			|	EXT_CALL zend_observer_fcall_begin, REG0
9639		}
9640
9641		if (trace) {
9642			if (!func && (opline->opcode != ZEND_DO_UCALL)) {
9643				|	b >9
9644			}
9645		} else {
9646#ifdef CONTEXT_THREADED_JIT
9647			|	NIY	// TODO: CONTEXT_THREADED_JIT is always undefined.
9648#else
9649			if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
9650				|	ADD_HYBRID_SPAD
9651				|	JMP_IP TMP1
9652			} else if (GCC_GLOBAL_REGS) {
9653				|	ldp x29, x30, [sp], # SPAD // stack alignment
9654				|	JMP_IP TMP1
9655			} else {
9656				|	ldp FP, RX, T2                // retore FP and IP
9657				|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
9658				|	mov RETVALx, #1     // ZEND_VM_ENTER
9659				|	ret
9660			}
9661		}
9662#endif
9663	}
9664
9665	if ((!func || func->type == ZEND_INTERNAL_FUNCTION)
9666	 && (opline->opcode != ZEND_DO_UCALL)) {
9667		if (!func && (opline->opcode != ZEND_DO_ICALL)) {
9668			|8:
9669		}
9670		if (opline->opcode == ZEND_DO_FCALL_BY_NAME) {
9671			if (!func) {
9672				if (trace) {
9673					uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9674
9675					exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9676					if (!exit_addr) {
9677						return 0;
9678					}
9679					|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
9680					|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
9681					|	bne &exit_addr
9682				} else {
9683					|	ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)]
9684					|	TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w
9685					|	bne >1
9686					|.cold_code
9687					|1:
9688					if (!GCC_GLOBAL_REGS) {
9689						|	mov FCARG1x, RX
9690					}
9691					|	EXT_CALL zend_jit_deprecated_helper, REG0
9692					|	GET_LOW_8BITS RETVALw, RETVALw
9693					|	ldr REG0, EX:RX->func // reload
9694					|	cbnz RETVALw, >1      // Result is 0 on exception
9695					|	b ->exception_handler
9696					|.code
9697					|1:
9698				}
9699			} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
9700				if (!GCC_GLOBAL_REGS) {
9701					|	mov FCARG1x, RX
9702				}
9703				|	EXT_CALL zend_jit_deprecated_helper, REG0
9704				|	cbz RETVALw, ->exception_handler
9705				|	ldr REG0, EX:RX->func // reload
9706			}
9707		}
9708
9709		|	// ZVAL_NULL(EX_VAR(opline->result.var));
9710		|	LOAD_ZVAL_ADDR FCARG2x, res_addr
9711		|	SET_Z_TYPE_INFO FCARG2x, IS_NULL, TMP1w
9712
9713		|	// EG(current_execute_data) = execute_data;
9714		|	MEM_STORE_64_ZTS str, RX, executor_globals, current_execute_data, REG1
9715
9716		zend_jit_reset_last_valid_opline();
9717
9718		|	// (zend_execute_internal ? zend_execute_internal : fbc->internal_function.handler)(call, ret);
9719		|	mov FCARG1x, RX
9720		if (zend_execute_internal) {
9721			|	EXT_CALL zend_execute_internal, REG0
9722		} else {
9723			if (func) {
9724				|	EXT_CALL func->internal_function.handler, REG0
9725			} else {
9726				|	ldr TMP1, [REG0, #offsetof(zend_internal_function, handler)]
9727				|	blr TMP1
9728			}
9729		}
9730
9731		|	// EG(current_execute_data) = execute_data;
9732		|	MEM_STORE_64_ZTS str, FP, executor_globals, current_execute_data, REG0
9733
9734		|	// zend_vm_stack_free_args(call);
9735		if (func && !unknown_num_args) {
9736			for (i = 0; i < call_num_args; i++ ) {
9737				if (zend_jit_needs_arg_dtor(func, i, call_info)) {
9738					uint32_t offset = EX_NUM_TO_VAR(i);
9739					zend_jit_addr arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, offset);
9740					|	ZVAL_PTR_DTOR arg_addr, (MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN), 0, 1, opline, ZREG_TMP1, ZREG_TMP2
9741				}
9742			}
9743		} else {
9744			|	mov FCARG1x, RX
9745			|	EXT_CALL zend_jit_vm_stack_free_args_helper, REG0
9746		}
9747		if (may_have_extra_named_params) {
9748		    |	ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 3)]
9749			|	TST_32_WITH_CONST TMP1w, (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS >> 24), TMP2w
9750			|	bne >1
9751			|.cold_code
9752			|1:
9753			|	ldr FCARG1x, [RX, #offsetof(zend_execute_data, extra_named_params)]
9754			|	EXT_CALL zend_free_extra_named_params, REG0
9755			|	b >2
9756			|.code
9757			|2:
9758		}
9759
9760		|8:
9761		if (opline->opcode == ZEND_DO_FCALL) {
9762			// TODO: optimize ???
9763			|	// if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS))
9764			|	ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 2)]
9765			|	TST_32_WITH_CONST TMP1w, (ZEND_CALL_RELEASE_THIS >> 16), TMP2w
9766			|	bne >1
9767			|.cold_code
9768			|1:
9769			|	add TMP1, RX, #offsetof(zend_execute_data, This)
9770			|	GET_Z_PTR FCARG1x, TMP1
9771			|	// OBJ_RELEASE(object);
9772			|	OBJ_RELEASE ZREG_FCARG1, >2, ZREG_TMP1, ZREG_TMP2
9773			|	b >2
9774			|.code
9775			|2:
9776		}
9777
9778		if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
9779		    !JIT_G(current_frame) ||
9780		    !JIT_G(current_frame)->call ||
9781		    !TRACE_FRAME_IS_NESTED(JIT_G(current_frame)->call) ||
9782		    prev_opline->opcode == ZEND_SEND_UNPACK ||
9783		    prev_opline->opcode == ZEND_SEND_ARRAY ||
9784			prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) {
9785
9786			|	// zend_vm_stack_free_call_frame(call);
9787			|	ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 2)]
9788			|	TST_32_WITH_CONST TMP1w, ((ZEND_CALL_ALLOCATED >> 16) & 0xff), TMP2w
9789			|	bne >1
9790			|.cold_code
9791			|1:
9792			|	mov FCARG1x, RX
9793			|	EXT_CALL zend_jit_free_call_frame, REG0
9794			|	b >1
9795			|.code
9796		}
9797		|	MEM_STORE_64_ZTS str, RX, executor_globals, vm_stack_top, REG0
9798		|1:
9799
9800		if (!RETURN_VALUE_USED(opline)) {
9801			zend_class_entry *ce;
9802			bool ce_is_instanceof;
9803			uint32_t func_info = call_info ?
9804				zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof) :
9805				(MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
9806
9807			/* If an exception is thrown, the return_value may stay at the
9808			 * original value of null. */
9809			func_info |= MAY_BE_NULL;
9810
9811			if (func_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
9812				|	ZVAL_PTR_DTOR res_addr, func_info, 1, 1, opline, ZREG_TMP1, ZREG_TMP2
9813			}
9814		}
9815
9816		|	// if (UNEXPECTED(EG(exception) != NULL)) {
9817		|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
9818		|	cbnz REG0, ->icall_throw_handler
9819
9820		// TODO: Can we avoid checking for interrupts after each call ???
9821		if (trace && last_valid_opline != opline) {
9822			int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM);
9823
9824			exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9825			if (!exit_addr) {
9826				return 0;
9827			}
9828		} else {
9829			exit_addr = NULL;
9830		}
9831		if (!zend_jit_check_timeout(Dst, opline + 1, exit_addr)) {
9832			return 0;
9833		}
9834
9835		if ((!trace || !func) && opline->opcode != ZEND_DO_ICALL) {
9836			|	LOAD_IP_ADDR (opline + 1)
9837		} else if (trace
9838		 && trace->op == ZEND_JIT_TRACE_END
9839		 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
9840			|	LOAD_IP_ADDR (opline + 1)
9841		}
9842	}
9843
9844	if (!func) {
9845		|9:
9846	}
9847
9848	return 1;
9849}
9850
9851static int zend_jit_send_val(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr)
9852{
9853	uint32_t arg_num = opline->op2.num;
9854	zend_jit_addr arg_addr;
9855
9856	ZEND_ASSERT(opline->opcode == ZEND_SEND_VAL || arg_num <= MAX_ARG_FLAG_NUM);
9857
9858	if (!zend_jit_reuse_ip(Dst)) {
9859		return 0;
9860	}
9861
9862	if (opline->opcode == ZEND_SEND_VAL_EX) {
9863		uint32_t mask = ZEND_SEND_BY_REF << ((arg_num + 3) * 2);
9864
9865		ZEND_ASSERT(arg_num <= MAX_ARG_FLAG_NUM);
9866
9867		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
9868		 && JIT_G(current_frame)
9869		 && JIT_G(current_frame)->call
9870		 && JIT_G(current_frame)->call->func) {
9871			if (ARG_MUST_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
9872				/* Don't generate code that always throws exception */
9873				return 0;
9874			}
9875		} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
9876			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
9877			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
9878			if (!exit_addr) {
9879				return 0;
9880			}
9881			|	ldr REG0, EX:RX->func
9882			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
9883			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
9884			|	bne &exit_addr
9885		} else {
9886			|	ldr REG0, EX:RX->func
9887			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
9888			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
9889			|	bne >1
9890			|.cold_code
9891			|1:
9892			if (Z_MODE(op1_addr) == IS_REG) {
9893				/* set type to avoid zval_ptr_dtor() on uninitialized value */
9894				zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
9895				|	SET_ZVAL_TYPE_INFO addr, IS_UNDEF, TMP1w, TMP2
9896			}
9897			|	SET_EX_OPLINE opline, REG0
9898			|	b ->throw_cannot_pass_by_ref
9899			|.code
9900		}
9901	}
9902
9903	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
9904
9905	if (opline->op1_type == IS_CONST) {
9906		zval *zv = RT_CONSTANT(opline, opline->op1);
9907
9908		|	ZVAL_COPY_CONST arg_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
9909		if (Z_REFCOUNTED_P(zv)) {
9910			|	ADDREF_CONST zv, REG0, TMP1
9911		}
9912	} else {
9913		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
9914	}
9915
9916	return 1;
9917}
9918
9919static int zend_jit_check_undef_args(dasm_State **Dst, const zend_op *opline)
9920{
9921	|	ldr FCARG1x, EX->call
9922	|	ldrb TMP1w, [FCARG1x, #(offsetof(zend_execute_data, This.u1.type_info) + 3)]
9923	|	TST_32_WITH_CONST TMP1w, (ZEND_CALL_MAY_HAVE_UNDEF >> 24), TMP2w
9924	|	bne >1
9925	|.cold_code
9926	|1:
9927	|	SET_EX_OPLINE opline, REG0
9928	|	EXT_CALL zend_handle_undef_args, REG0
9929	|	cbz RETVALw, >2
9930	|	b ->exception_handler
9931	|.code
9932	|2:
9933
9934	return 1;
9935}
9936
9937static int zend_jit_send_ref(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, int cold)
9938{
9939	zend_jit_addr op1_addr, arg_addr, ref_addr;
9940
9941	op1_addr = OP1_ADDR();
9942	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
9943
9944	if (!zend_jit_reuse_ip(Dst)) {
9945		return 0;
9946	}
9947
9948	if (opline->op1_type == IS_VAR) {
9949		if (op1_info & MAY_BE_INDIRECT) {
9950			|	LOAD_ZVAL_ADDR REG0, op1_addr
9951			|	// if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) {
9952			|	IF_NOT_Z_TYPE REG0, IS_INDIRECT, >1, TMP1w
9953			|	// ret = Z_INDIRECT_P(ret);
9954			|	GET_Z_PTR REG0, REG0
9955			|1:
9956			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
9957		}
9958	} else if (opline->op1_type == IS_CV) {
9959		if (op1_info & MAY_BE_UNDEF) {
9960			if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
9961				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
9962				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2
9963				|	b >2
9964				|1:
9965			}
9966			op1_info &= ~MAY_BE_UNDEF;
9967			op1_info |= MAY_BE_NULL;
9968		}
9969	} else {
9970		ZEND_UNREACHABLE();
9971	}
9972
9973	if (op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) {
9974		if (op1_info & MAY_BE_REF) {
9975			|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >2, ZREG_TMP1
9976			|	GET_ZVAL_PTR REG1, op1_addr, TMP1
9977			|	GC_ADDREF REG1, TMP1w
9978			|	SET_ZVAL_PTR arg_addr, REG1, TMP1
9979			|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX, TMP1w, TMP2
9980			|	b >6
9981		}
9982		|2:
9983		|	// ZVAL_NEW_REF(arg, varptr);
9984		if (opline->op1_type == IS_VAR) {
9985			if (Z_REG(op1_addr) != ZREG_REG0 || Z_OFFSET(op1_addr) != 0) {
9986				|	LOAD_ZVAL_ADDR REG0, op1_addr
9987			}
9988			|	str REG0, T1  // save
9989		}
9990		|	EMALLOC sizeof(zend_reference), op_array, opline  // Allocate space in REG0
9991		|	mov TMP1w, #2
9992		|	str TMP1w, [REG0]
9993		||	ZEND_ASSERT(GC_REFERENCE <= MOVZ_IMM);
9994		|	movz TMP1w, #GC_REFERENCE
9995		|	str TMP1w, [REG0, #offsetof(zend_reference, gc.u.type_info)]
9996		|	str xzr, [REG0, #offsetof(zend_reference, sources.ptr)]
9997		ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, offsetof(zend_reference, val));
9998		if (opline->op1_type == IS_VAR) {
9999			zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG1, 0);
10000
10001			|	ldr REG1, T1  // restore
10002			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_REG2, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10003			|	SET_ZVAL_PTR val_addr, REG0, TMP1
10004			|	SET_ZVAL_TYPE_INFO val_addr, IS_REFERENCE_EX, TMP1w, TMP2
10005		} else {
10006			|	ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10007			|	SET_ZVAL_PTR op1_addr, REG0, TMP1
10008			|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2
10009		}
10010		|	SET_ZVAL_PTR arg_addr, REG0, TMP1
10011		|	SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX, TMP1w, TMP2
10012	}
10013
10014	|6:
10015	|	FREE_OP opline->op1_type, opline->op1, op1_info, !cold, opline, ZREG_TMP1, ZREG_TMP2
10016	|7:
10017
10018	return 1;
10019}
10020
10021static 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)
10022{
10023	uint32_t arg_num = opline->op2.num;
10024	zend_jit_addr arg_addr;
10025
10026	ZEND_ASSERT((opline->opcode != ZEND_SEND_VAR_EX &&
10027	     opline->opcode != ZEND_SEND_VAR_NO_REF_EX) ||
10028	    arg_num <= MAX_ARG_FLAG_NUM);
10029
10030	arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var);
10031
10032	if (!zend_jit_reuse_ip(Dst)) {
10033		return 0;
10034	}
10035
10036	if (opline->opcode == ZEND_SEND_VAR_EX) {
10037		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10038		 && JIT_G(current_frame)
10039		 && JIT_G(current_frame)->call
10040		 && JIT_G(current_frame)->call->func) {
10041			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10042				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
10043					return 0;
10044				}
10045				return 1;
10046			}
10047		} else {
10048			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10049
10050			|	ldr REG0, EX:RX->func
10051			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
10052			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
10053			|	bne >1
10054			|.cold_code
10055			|1:
10056			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
10057				return 0;
10058			}
10059			|	b >7
10060			|.code
10061		}
10062	} else if (opline->opcode == ZEND_SEND_VAR_NO_REF_EX) {
10063		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10064		 && JIT_G(current_frame)
10065		 && JIT_G(current_frame)->call
10066		 && JIT_G(current_frame)->call->func) {
10067			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10068
10069				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10070
10071				if (!ARG_MAY_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10072					if (!(op1_info & MAY_BE_REF)) {
10073						/* Don't generate code that always throws exception */
10074						return 0;
10075					} else {
10076						int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10077						const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10078						if (!exit_addr) {
10079							return 0;
10080						}
10081						|	GET_LOW_8BITS TMP1w, REG1w
10082						|	cmp TMP1w, #IS_REFERENCE
10083						|	bne &exit_addr
10084					}
10085				}
10086				return 1;
10087			}
10088		} else {
10089			uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10090
10091			|	ldr REG0, EX:RX->func
10092			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
10093			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
10094			|	bne >1
10095			|.cold_code
10096			|1:
10097
10098			mask = ZEND_SEND_PREFER_REF << ((arg_num + 3) * 2);
10099
10100			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10101			if (op1_info & MAY_BE_REF) {
10102				|	GET_LOW_8BITS TMP1w, REG1w
10103				|	cmp TMP1w, #IS_REFERENCE
10104				|	beq >7
10105			}
10106			|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
10107			|	TST_32_WITH_CONST TMP1w, mask, TMP2w
10108			|	bne >7
10109			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10110				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10111				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10112				if (!exit_addr) {
10113					return 0;
10114				}
10115				|	b &exit_addr
10116			} else {
10117				|	SET_EX_OPLINE opline, REG0
10118				|	LOAD_ZVAL_ADDR FCARG1x, arg_addr
10119				|	EXT_CALL zend_jit_only_vars_by_reference, REG0
10120				if (!zend_jit_check_exception(Dst)) {
10121					return 0;
10122				}
10123				|	b >7
10124			}
10125
10126			|.code
10127		}
10128	} else if (opline->opcode == ZEND_SEND_FUNC_ARG) {
10129		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10130		 && JIT_G(current_frame)
10131		 && JIT_G(current_frame)->call
10132		 && JIT_G(current_frame)->call->func) {
10133			if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10134				if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) {
10135					return 0;
10136				}
10137				return 1;
10138			}
10139		} else {
10140			|	ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10141			|	TST_32_WITH_CONST TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
10142			|	bne >1
10143			|.cold_code
10144			|1:
10145			if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) {
10146				return 0;
10147			}
10148			|	b >7
10149			|.code
10150		}
10151	}
10152
10153	if (op1_info & MAY_BE_UNDEF) {
10154		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10155			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
10156			|.cold_code
10157			|1:
10158		}
10159
10160		|	SET_EX_OPLINE opline, REG0
10161		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
10162		|	EXT_CALL zend_jit_undefined_op_helper, REG0
10163		|	SET_ZVAL_TYPE_INFO arg_addr, IS_NULL, TMP1w, TMP2
10164		|	cbz RETVALx, ->exception_handler
10165
10166		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10167			|	b >7
10168			|.code
10169		} else {
10170			|7:
10171			return 1;
10172		}
10173	}
10174
10175	if (opline->opcode == ZEND_SEND_VAR_NO_REF) {
10176		|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10177		if (op1_info & MAY_BE_REF) {
10178			|	GET_LOW_8BITS TMP1w, REG1w
10179			|	cmp TMP1w, #IS_REFERENCE
10180			|	beq >7
10181		}
10182		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
10183			int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10184			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10185			if (!exit_addr) {
10186				return 0;
10187			}
10188			|	b &exit_addr
10189		} else {
10190			|	SET_EX_OPLINE opline, REG0
10191			|	LOAD_ZVAL_ADDR FCARG1x, arg_addr
10192			|	EXT_CALL zend_jit_only_vars_by_reference, REG0
10193			if (!zend_jit_check_exception(Dst)) {
10194				return 0;
10195			}
10196		}
10197	} else {
10198		if (op1_info & MAY_BE_REF) {
10199			if (opline->op1_type == IS_CV) {
10200				zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
10201
10202				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
10203				|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
10204				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10205				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
10206			} else {
10207				zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 8);
10208
10209				|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1
10210				|.cold_code
10211				|1:
10212				|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
10213				|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
10214				|	// ZVAL_COPY_VALUE(return_value, &ref->value);
10215				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10216				|	GC_DELREF FCARG1x, TMP1w
10217				|	beq >1
10218				|	IF_NOT_REFCOUNTED REG0w, >2, TMP1w
10219				|	GC_ADDREF REG2, TMP1w
10220				|	b >2
10221				|1:
10222				|	EFREE_REFERENCE
10223				|	b >2
10224				|.code
10225				|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10226				|2:
10227			}
10228		} else {
10229			if (op1_addr != op1_def_addr) {
10230				if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) {
10231					return 0;
10232				}
10233				if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) {
10234					op1_addr= op1_def_addr;
10235				}
10236			}
10237			|	ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
10238			if (opline->op1_type == IS_CV) {
10239				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
10240			}
10241		}
10242	}
10243	|7:
10244
10245	return 1;
10246}
10247
10248static int zend_jit_check_func_arg(dasm_State **Dst, const zend_op *opline)
10249{
10250	uint32_t arg_num = opline->op2.num;
10251
10252	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
10253	 && JIT_G(current_frame)
10254	 && JIT_G(current_frame)->call
10255	 && JIT_G(current_frame)->call->func) {
10256		if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) {
10257			if (!TRACE_FRAME_IS_LAST_SEND_BY_REF(JIT_G(current_frame)->call)) {
10258				TRACE_FRAME_SET_LAST_SEND_BY_REF(JIT_G(current_frame)->call);
10259				|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10260				||	if (reuse_ip) {
10261				|		ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10262				|		BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
10263				|		str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10264				||	} else {
10265				|		ldr REG0, EX->call
10266				|		ldr TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
10267				|		BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
10268				|		str TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
10269				||	}
10270			}
10271		} else {
10272			if (!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
10273				TRACE_FRAME_SET_LAST_SEND_BY_VAL(JIT_G(current_frame)->call);
10274				|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10275				||	if (reuse_ip) {
10276				|		ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10277				|		BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~ZEND_CALL_SEND_ARG_BY_REF), TMP2w
10278				|		str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10279				||	} else {
10280				|		ldr REG0, EX->call
10281				|		ldr TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
10282				|		BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~ZEND_CALL_SEND_ARG_BY_REF), TMP2w
10283				|		str TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)]
10284				||	}
10285			}
10286		}
10287	} else {
10288		// if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
10289		uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2);
10290
10291		if (!zend_jit_reuse_ip(Dst)) {
10292			return 0;
10293		}
10294
10295		|	ldr REG0, EX:RX->func
10296		|	ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)]
10297		|	TST_32_WITH_CONST TMP1w, mask, TMP2w
10298		|	bne >1
10299		|.cold_code
10300		|1:
10301		|	// ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10302		|	ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10303		|	BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w
10304		|	str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10305		|	b >1
10306		|.code
10307		|	// ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
10308		|	ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10309		|	BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~(ZEND_CALL_SEND_ARG_BY_REF)), TMP2w
10310		|	str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)]
10311		|1:
10312	}
10313
10314	return 1;
10315}
10316
10317static 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)
10318{
10319	if (smart_branch_opcode) {
10320		if (smart_branch_opcode == ZEND_JMPZ) {
10321			if (jmp) {
10322				|	b >7
10323			}
10324		} else if (smart_branch_opcode == ZEND_JMPNZ) {
10325			|	b =>target_label
10326		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
10327			|	b =>target_label2
10328		} else {
10329			ZEND_UNREACHABLE();
10330		}
10331	} else {
10332		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
10333
10334		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
10335		if (jmp) {
10336			|	b >7
10337		}
10338	}
10339
10340	return 1;
10341}
10342
10343static int zend_jit_smart_false(dasm_State **Dst, const zend_op *opline, int jmp, zend_uchar smart_branch_opcode, uint32_t target_label)
10344{
10345	if (smart_branch_opcode) {
10346		if (smart_branch_opcode == ZEND_JMPZ) {
10347			|	b =>target_label
10348		} else if (smart_branch_opcode == ZEND_JMPNZ) {
10349			if (jmp) {
10350				|	b >7
10351			}
10352		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
10353			|	b =>target_label
10354		} else {
10355			ZEND_UNREACHABLE();
10356		}
10357	} else {
10358		zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
10359
10360		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
10361		if (jmp) {
10362			|	b >7
10363		}
10364	}
10365
10366	return 1;
10367}
10368
10369static 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)
10370{
10371	uint32_t defined_label = (uint32_t)-1;
10372	uint32_t undefined_label = (uint32_t)-1;
10373	zval *zv = RT_CONSTANT(opline, opline->op1);
10374	zend_jit_addr res_addr = 0;
10375
10376	if (smart_branch_opcode && !exit_addr) {
10377		if (smart_branch_opcode == ZEND_JMPZ) {
10378			undefined_label = target_label;
10379		} else if (smart_branch_opcode == ZEND_JMPNZ) {
10380			defined_label = target_label;
10381		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
10382			undefined_label = target_label;
10383			defined_label = target_label2;
10384		} else {
10385			ZEND_UNREACHABLE();
10386		}
10387	}
10388
10389	|	// if (CACHED_PTR(opline->extended_value)) {
10390	|	ldr REG0, EX->run_time_cache
10391	|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, opline->extended_value, TMP1
10392	|	cbz REG0, >1
10393	|	TST_64_WITH_ONE REG0
10394	|	bne >4
10395	|.cold_code
10396	|4:
10397	|	MEM_LOAD_64_ZTS ldr, FCARG1x, executor_globals, zend_constants, FCARG1x
10398	|	ldr TMP1w, [FCARG1x, #offsetof(HashTable, nNumOfElements)]
10399	|	cmp TMP1, REG0, lsr #1
10400
10401	if (smart_branch_opcode) {
10402		if (exit_addr) {
10403			if (smart_branch_opcode == ZEND_JMPZ) {
10404				|	beq &exit_addr
10405			} else {
10406				|	beq >3
10407			}
10408		} else if (undefined_label != (uint32_t)-1) {
10409			|	beq =>undefined_label
10410		} else {
10411			|	beq >3
10412		}
10413	} else {
10414		|	beq >2
10415	}
10416	|1:
10417	|	SET_EX_OPLINE opline, REG0
10418	|	LOAD_ADDR FCARG1x, zv
10419	|	EXT_CALL zend_jit_check_constant, REG0
10420	if (exit_addr) {
10421		if (smart_branch_opcode == ZEND_JMPNZ) {
10422			|	cbz RETVALx, >3
10423		} else {
10424			|	cbnz RETVALx, >3
10425		}
10426		|	b &exit_addr
10427	} else if (smart_branch_opcode) {
10428		if (undefined_label != (uint32_t)-1) {
10429			|	cbz RETVALx, =>undefined_label
10430		} else {
10431			|	cbz RETVALx, >3
10432		}
10433		if (defined_label != (uint32_t)-1) {
10434			|	b =>defined_label
10435		} else {
10436			|	b >3
10437		}
10438	} else {
10439		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
10440		|	cbnz RETVALx, >1
10441		|2:
10442		|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
10443		|	b >3
10444	}
10445	|.code
10446	if (smart_branch_opcode) {
10447		if (exit_addr) {
10448			if (smart_branch_opcode == ZEND_JMPNZ) {
10449				|	b &exit_addr
10450			}
10451		} else if (defined_label != (uint32_t)-1) {
10452			|	b =>defined_label
10453		}
10454	} else {
10455		|1:
10456		|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
10457	}
10458	|3:
10459
10460	return 1;
10461}
10462
10463static 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)
10464{
10465	uint32_t  mask;
10466	zend_jit_addr op1_addr = OP1_ADDR();
10467
10468	// TODO: support for is_resource() ???
10469	ZEND_ASSERT(opline->extended_value != MAY_BE_RESOURCE);
10470
10471	if (op1_info & MAY_BE_UNDEF) {
10472		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10473			|	IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
10474			|.cold_code
10475			|1:
10476		}
10477		|	SET_EX_OPLINE opline, REG0
10478		|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
10479		|	EXT_CALL zend_jit_undefined_op_helper, REG0
10480		zend_jit_check_exception_undef_result(Dst, opline);
10481		if (opline->extended_value & MAY_BE_NULL) {
10482			if (exit_addr) {
10483				if (smart_branch_opcode == ZEND_JMPNZ) {
10484					|	b &exit_addr
10485				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
10486					|	b >7
10487				}
10488			} else if (!zend_jit_smart_true(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label, target_label2)) {
10489				return 0;
10490			}
10491		} else {
10492			if (exit_addr) {
10493				if (smart_branch_opcode == ZEND_JMPZ) {
10494					|	b &exit_addr
10495				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) {
10496					|	b >7
10497				}
10498			} else if (!zend_jit_smart_false(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label)) {
10499				return 0;
10500			}
10501		}
10502		if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10503			|.code
10504		}
10505	}
10506
10507	if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) {
10508		mask = opline->extended_value;
10509		if (!(op1_info & MAY_BE_GUARD) && !(op1_info & (MAY_BE_ANY - mask))) {
10510			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
10511			if (exit_addr) {
10512				if (smart_branch_opcode == ZEND_JMPNZ) {
10513					|	b &exit_addr
10514				}
10515			} else if (!zend_jit_smart_true(Dst, opline, 0, smart_branch_opcode, target_label, target_label2)) {
10516				return 0;
10517			}
10518	    } else if (!(op1_info & MAY_BE_GUARD) && !(op1_info & mask)) {
10519			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
10520			if (exit_addr) {
10521				if (smart_branch_opcode == ZEND_JMPZ) {
10522					|	b &exit_addr
10523				}
10524			} else if (!zend_jit_smart_false(Dst, opline, 0, smart_branch_opcode, target_label)) {
10525				return 0;
10526			}
10527		} else {
10528			bool invert = 0;
10529			zend_uchar type;
10530
10531			switch (mask) {
10532				case MAY_BE_NULL:   type = IS_NULL;   break;
10533				case MAY_BE_FALSE:  type = IS_FALSE;  break;
10534				case MAY_BE_TRUE:   type = IS_TRUE;   break;
10535				case MAY_BE_LONG:   type = IS_LONG;   break;
10536				case MAY_BE_DOUBLE: type = IS_DOUBLE; break;
10537				case MAY_BE_STRING: type = IS_STRING; break;
10538				case MAY_BE_ARRAY:  type = IS_ARRAY;  break;
10539				case MAY_BE_OBJECT: type = IS_OBJECT; break;
10540				case MAY_BE_ANY - MAY_BE_NULL:     type = IS_NULL;   invert = 1; break;
10541				case MAY_BE_ANY - MAY_BE_FALSE:    type = IS_FALSE;  invert = 1; break;
10542				case MAY_BE_ANY - MAY_BE_TRUE:     type = IS_TRUE;   invert = 1; break;
10543				case MAY_BE_ANY - MAY_BE_LONG:     type = IS_LONG;   invert = 1; break;
10544				case MAY_BE_ANY - MAY_BE_DOUBLE:   type = IS_DOUBLE; invert = 1; break;
10545				case MAY_BE_ANY - MAY_BE_STRING:   type = IS_STRING; invert = 1; break;
10546				case MAY_BE_ANY - MAY_BE_ARRAY:    type = IS_ARRAY;  invert = 1; break;
10547				case MAY_BE_ANY - MAY_BE_OBJECT:   type = IS_OBJECT; invert = 1; break;
10548				case MAY_BE_ANY - MAY_BE_RESOURCE: type = IS_OBJECT; invert = 1; break;
10549				default:
10550					type = 0;
10551			}
10552
10553			if (op1_info & MAY_BE_REF) {
10554				|	LOAD_ZVAL_ADDR REG0, op1_addr
10555				|	ZVAL_DEREF REG0, op1_info, TMP1w
10556			}
10557			if (type == 0) {
10558				if (smart_branch_opcode &&
10559				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
10560				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
10561					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
10562						|	// if (Z_REFCOUNTED_P(cv)) {
10563						|	IF_ZVAL_REFCOUNTED op1_addr, >1, ZREG_TMP1, ZREG_TMP2
10564						|.cold_code
10565						|1:
10566					}
10567					|	// if (!Z_DELREF_P(cv)) {
10568					|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
10569					|	GC_DELREF FCARG1x, TMP1w
10570					if (RC_MAY_BE_1(op1_info)) {
10571						if (RC_MAY_BE_N(op1_info)) {
10572							|	bne >3
10573						}
10574						if (op1_info & MAY_BE_REF) {
10575							|	ldrb REG0w, [REG0, #offsetof(zval,u1.v.type)]
10576						} else {
10577							|	MEM_ACCESS_8_WITH_UOFFSET ldrb, REG0w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
10578						}
10579						|	str REG0w, T1 // save
10580						|	// zval_dtor_func(r);
10581						|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
10582						|	ldr REG1w, T1 // restore
10583						|	b >2
10584					}
10585					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
10586						if (!RC_MAY_BE_1(op1_info)) {
10587							|	b >3
10588						}
10589						|.code
10590					}
10591					|3:
10592					if (op1_info & MAY_BE_REF) {
10593						|	ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)]
10594					} else {
10595						|	MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
10596					}
10597					|2:
10598				} else {
10599					if (op1_info & MAY_BE_REF) {
10600						|	ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)]
10601					} else {
10602						|	MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
10603					}
10604				}
10605				|	mov REG0w, #1
10606				|	lsl REG0w, REG0w, REG1w
10607				|	TST_32_WITH_CONST REG0w, mask, TMP1w
10608				if (exit_addr) {
10609					if (smart_branch_opcode == ZEND_JMPNZ) {
10610						|	bne &exit_addr
10611					} else {
10612						|	beq &exit_addr
10613					}
10614				} else if (smart_branch_opcode) {
10615					if (smart_branch_opcode == ZEND_JMPZ) {
10616						|	beq =>target_label
10617					} else if (smart_branch_opcode == ZEND_JMPNZ) {
10618						|	bne =>target_label
10619					} else if (smart_branch_opcode == ZEND_JMPZNZ) {
10620						|	beq =>target_label
10621						|	b =>target_label2
10622					} else {
10623						ZEND_UNREACHABLE();
10624					}
10625				} else {
10626					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
10627
10628					|	cset REG0w, ne
10629					|	add REG0w, REG0w, #2
10630					|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
10631					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
10632				}
10633			} else {
10634				if (smart_branch_opcode &&
10635				    (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
10636				    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
10637					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
10638						|	// if (Z_REFCOUNTED_P(cv)) {
10639						|	IF_ZVAL_REFCOUNTED op1_addr, >1, ZREG_TMP1, ZREG_TMP2
10640						|.cold_code
10641						|1:
10642					}
10643					|	// if (!Z_DELREF_P(cv)) {
10644					|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
10645					|	GC_DELREF FCARG1x, TMP1w
10646					if (RC_MAY_BE_1(op1_info)) {
10647						if (RC_MAY_BE_N(op1_info)) {
10648							|	bne >3
10649						}
10650						if (op1_info & MAY_BE_REF) {
10651							|	ldrb REG0w, [REG0, #offsetof(zval,u1.v.type)]
10652						} else {
10653							|	MEM_ACCESS_8_WITH_UOFFSET ldrb, REG0w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
10654						}
10655						|	str REG0w, T1 // save
10656						|	// zval_dtor_func(r);
10657						|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
10658						|	ldr REG1w, T1 // restore
10659						|	b >2
10660					}
10661					if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
10662						if (!RC_MAY_BE_1(op1_info)) {
10663							|	b >3
10664						}
10665						|.code
10666					}
10667					|3:
10668					if (op1_info & MAY_BE_REF) {
10669						|	ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)]
10670					} else {
10671						|	MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
10672					}
10673					|2:
10674					// Note: 'type' is of uchar type and holds a positive value,
10675					// hence it's safe to directly encode it as the imm field of 'cmp' instruction.
10676					|	cmp REG1w, #type
10677				} else {
10678					if (op1_info & MAY_BE_REF) {
10679						|	ldrb TMP1w, [REG0, #offsetof(zval,u1.v.type)]
10680						|	cmp TMP1w, #type
10681					} else {
10682						|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1
10683						|	cmp TMP1w, #type
10684					}
10685				}
10686				if (exit_addr) {
10687					if (invert) {
10688						if (smart_branch_opcode == ZEND_JMPNZ) {
10689							|	bne &exit_addr
10690						} else {
10691							|	beq &exit_addr
10692						}
10693					} else {
10694						if (smart_branch_opcode == ZEND_JMPNZ) {
10695							|	beq &exit_addr
10696						} else {
10697							|	bne &exit_addr
10698						}
10699					}
10700				} else if (smart_branch_opcode) {
10701					if (invert) {
10702						if (smart_branch_opcode == ZEND_JMPZ) {
10703							|	beq =>target_label
10704						} else if (smart_branch_opcode == ZEND_JMPNZ) {
10705							|	bne =>target_label
10706						} else if (smart_branch_opcode == ZEND_JMPZNZ) {
10707							|	beq =>target_label
10708							|	b =>target_label2
10709						} else {
10710							ZEND_UNREACHABLE();
10711						}
10712					} else {
10713						if (smart_branch_opcode == ZEND_JMPZ) {
10714							|	bne =>target_label
10715						} else if (smart_branch_opcode == ZEND_JMPNZ) {
10716							|	beq =>target_label
10717						} else if (smart_branch_opcode == ZEND_JMPZNZ) {
10718							|	bne =>target_label
10719							|	b =>target_label2
10720						} else {
10721							ZEND_UNREACHABLE();
10722						}
10723					}
10724				} else {
10725					zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
10726
10727					if (invert) {
10728						|	cset REG0w, ne
10729					} else {
10730						|	cset REG0w, eq
10731					}
10732					|	add REG0w, REG0w, #2
10733					|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
10734					|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
10735				}
10736			}
10737	    }
10738	}
10739
10740	|7:
10741
10742	return 1;
10743}
10744
10745static int zend_jit_leave_frame(dasm_State **Dst)
10746{
10747	|	// EG(current_execute_data) = EX(prev_execute_data);
10748	|	ldr REG0, EX->prev_execute_data
10749	|	MEM_STORE_64_ZTS str, REG0, executor_globals, current_execute_data, REG2
10750	return 1;
10751}
10752
10753static int zend_jit_free_cvs(dasm_State **Dst)
10754{
10755	|	// EG(current_execute_data) = EX(prev_execute_data);
10756	|	ldr FCARG1x, EX->prev_execute_data
10757	|	MEM_STORE_64_ZTS str, FCARG1x, executor_globals, current_execute_data, REG0
10758	|	// zend_free_compiled_variables(execute_data);
10759	|	mov FCARG1x, FP
10760	|	EXT_CALL zend_free_compiled_variables, REG0
10761	return 1;
10762}
10763
10764static int zend_jit_free_cv(dasm_State **Dst, uint32_t info, uint32_t var)
10765{
10766	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
10767		uint32_t offset = EX_NUM_TO_VAR(var);
10768		zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offset);
10769		|	ZVAL_PTR_DTOR addr, info, 1, 1, NULL, ZREG_TMP1, ZREG_TMP2
10770	}
10771	return 1;
10772}
10773
10774static int zend_jit_free_op(dasm_State **Dst, const zend_op *opline, uint32_t info, uint32_t var_offset)
10775{
10776	if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
10777		zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var_offset);
10778		|	ZVAL_PTR_DTOR addr, info, 0, 1, opline, ZREG_TMP1, ZREG_TMP2
10779	}
10780	return 1;
10781}
10782
10783static int zend_jit_leave_func(dasm_State          **Dst,
10784                               const zend_op_array  *op_array,
10785                               const zend_op        *opline,
10786                               uint32_t              op1_info,
10787                               bool                  left_frame,
10788                               zend_jit_trace_rec   *trace,
10789                               zend_jit_trace_info  *trace_info,
10790                               int                   indirect_var_access,
10791                               int                   may_throw)
10792{
10793	bool may_be_top_frame =
10794		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
10795		!JIT_G(current_frame) ||
10796		!TRACE_FRAME_IS_NESTED(JIT_G(current_frame));
10797	bool may_need_call_helper =
10798		indirect_var_access || /* may have symbol table */
10799		!op_array->function_name || /* may have symbol table */
10800		may_be_top_frame ||
10801		(op_array->fn_flags & ZEND_ACC_VARIADIC) || /* may have extra named args */
10802		JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
10803		!JIT_G(current_frame) ||
10804		TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) == -1 || /* unknown number of args */
10805		(uint32_t)TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) > op_array->num_args; /* extra args */
10806	bool may_need_release_this =
10807		!(op_array->fn_flags & ZEND_ACC_CLOSURE) &&
10808		op_array->scope &&
10809		!(op_array->fn_flags & ZEND_ACC_STATIC) &&
10810		(JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
10811		 !JIT_G(current_frame) ||
10812		 !TRACE_FRAME_NO_NEED_RELEASE_THIS(JIT_G(current_frame)));
10813
10814	if (may_need_call_helper || may_need_release_this) {
10815		|	ldr FCARG1w, [FP, #offsetof(zend_execute_data, This.u1.type_info)]
10816	}
10817	if (may_need_call_helper) {
10818		if (!left_frame) {
10819			left_frame = 1;
10820		    if (!zend_jit_leave_frame(Dst)) {
10821				return 0;
10822		    }
10823		}
10824		/* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */
10825
10826		|	TST_32_WITH_CONST FCARG1w, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_FAKE_CLOSURE), TMP1w
10827		if (trace && trace->op != ZEND_JIT_TRACE_END) {
10828			|	bne >1
10829			|.cold_code
10830			|1:
10831			if (!GCC_GLOBAL_REGS) {
10832				|	mov FCARG1x, FP
10833			}
10834			|	EXT_CALL zend_jit_leave_func_helper, REG0
10835
10836			if (may_be_top_frame) {
10837				// TODO: try to avoid this check ???
10838				if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
10839#if 0
10840					/* this check should be handled by the following OPLINE guard */
10841					|	LOAD_ADDR TMP1, zend_jit_halt_op
10842					|	cmp IP, TMP1
10843					|	beq ->trace_halt
10844#endif
10845				} else if (GCC_GLOBAL_REGS) {
10846					|	cbz IP, ->trace_halt
10847				} else {
10848					|	tst RETVALw, RETVALw
10849					|	blt ->trace_halt
10850				}
10851			}
10852
10853			if (!GCC_GLOBAL_REGS) {
10854				|	// execute_data = EG(current_execute_data)
10855				|	MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, TMP1
10856			}
10857			|	b >8
10858			|.code
10859		} else {
10860			|	bne ->leave_function_handler
10861		}
10862	}
10863
10864	if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
10865		if (!left_frame) {
10866			left_frame = 1;
10867		    if (!zend_jit_leave_frame(Dst)) {
10868				return 0;
10869		    }
10870		}
10871		|	// OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
10872		|	ldr FCARG1x, EX->func
10873		|	sub FCARG1x, FCARG1x, #sizeof(zend_object)
10874		|	OBJ_RELEASE ZREG_FCARG1, >4, ZREG_TMP1, ZREG_TMP2
10875		|4:
10876	} else if (may_need_release_this) {
10877		if (!left_frame) {
10878			left_frame = 1;
10879		    if (!zend_jit_leave_frame(Dst)) {
10880				return 0;
10881		    }
10882		}
10883		|	// if (call_info & ZEND_CALL_RELEASE_THIS)
10884		|	TST_32_WITH_CONST FCARG1w, ZEND_CALL_RELEASE_THIS, TMP1w
10885		|	beq >4
10886		|	// zend_object *object = Z_OBJ(execute_data->This);
10887		|	ldr FCARG1x, EX->This.value.obj
10888		|	// OBJ_RELEASE(object);
10889		|	OBJ_RELEASE ZREG_FCARG1, >4, ZREG_TMP1, ZREG_TMP2
10890		|4:
10891		// TODO: avoid EG(excption) check for $this->foo() calls
10892		may_throw = 1;
10893	}
10894
10895	|	// EG(vm_stack_top) = (zval*)execute_data;
10896	|	MEM_STORE_64_ZTS str, FP, executor_globals, vm_stack_top, REG0
10897	|	// execute_data = EX(prev_execute_data);
10898	|	ldr FP, EX->prev_execute_data
10899
10900	if (!left_frame) {
10901		|	// EG(current_execute_data) = execute_data;
10902		|	MEM_STORE_64_ZTS str, FP, executor_globals, current_execute_data, REG0
10903	}
10904
10905	|9:
10906	if (trace) {
10907		if (trace->op != ZEND_JIT_TRACE_END
10908		 && (JIT_G(current_frame) && !TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
10909			zend_jit_reset_last_valid_opline();
10910		} else {
10911			|	LOAD_IP
10912			|	ADD_IP_WITH_CONST sizeof(zend_op), TMP1
10913		}
10914
10915		|8:
10916
10917		if (trace->op == ZEND_JIT_TRACE_BACK
10918		 && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) {
10919			const zend_op *next_opline = trace->opline;
10920
10921			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
10922			 && (op1_info & MAY_BE_RC1)
10923			 && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
10924				/* exception might be thrown during destruction of unused return value */
10925				|	// if (EG(exception))
10926				|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
10927				|	cbnz REG0, ->leave_throw_handler
10928			}
10929			do {
10930				trace++;
10931			} while (trace->op == ZEND_JIT_TRACE_INIT_CALL);
10932			ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
10933			next_opline = trace->opline;
10934			ZEND_ASSERT(next_opline != NULL);
10935
10936			if (trace->op == ZEND_JIT_TRACE_END
10937			 && trace->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
10938				trace_info->flags |= ZEND_JIT_TRACE_LOOP;
10939				|	CMP_IP next_opline, TMP1, TMP2
10940				|	beq =>0 // LOOP
10941#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE
10942				|	JMP_IP TMP1
10943#else
10944				|	b ->trace_escape
10945#endif
10946			} else {
10947				|	CMP_IP next_opline, TMP1, TMP2
10948				|	bne ->trace_escape
10949			}
10950
10951			zend_jit_set_last_valid_opline(trace->opline);
10952
10953			return 1;
10954		} else if (may_throw ||
10955				(((opline->op1_type & (IS_VAR|IS_TMP_VAR))
10956				  && (op1_info & MAY_BE_RC1)
10957				  && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)))
10958				 && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) {
10959			|	// if (EG(exception))
10960			|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
10961			|	cbnz REG0, ->leave_throw_handler
10962		}
10963
10964		return 1;
10965	} else {
10966		|	// if (EG(exception))
10967		|	MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1
10968		|	LOAD_IP
10969		|	cbnz REG0, ->leave_throw_handler
10970		|	// opline = EX(opline) + 1
10971		|	ADD_IP_WITH_CONST sizeof(zend_op), TMP1
10972	}
10973
10974	if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
10975		|	ADD_HYBRID_SPAD
10976#ifdef CONTEXT_THREADED_JIT
10977		|	NIY	// TODO: CONTEXT_THREADED_JIT is always undefined
10978#else
10979		|	JMP_IP TMP1
10980#endif
10981	} else if (GCC_GLOBAL_REGS) {
10982		|	ldp x29, x30, [sp], # SPAD // stack alignment
10983#ifdef CONTEXT_THREADED_JIT
10984		|	NIY	// TODO
10985#else
10986		|	JMP_IP TMP1
10987#endif
10988	} else {
10989#ifdef CONTEXT_THREADED_JIT
10990		ZEND_UNREACHABLE();
10991		// TODO: context threading can't work without GLOBAL REGS because we have to change
10992		//       the value of execute_data in execute_ex()
10993		|	NIY	// TODO
10994#else
10995		|	ldp FP, RX, T2                // retore FP and IP
10996		|	ldp x29, x30, [sp], # NR_SPAD // stack alignment
10997		|	mov RETVALx, #2               // ZEND_VM_LEAVE ????
10998		|	ret
10999#endif
11000	}
11001
11002	return 1;
11003}
11004
11005static 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)
11006{
11007	zend_jit_addr ret_addr;
11008	int8_t return_value_used;
11009
11010	ZEND_ASSERT(op_array->type != ZEND_EVAL_CODE && op_array->function_name);
11011	ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF));
11012
11013	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && JIT_G(current_frame)) {
11014		if (TRACE_FRAME_IS_RETURN_VALUE_USED(JIT_G(current_frame))) {
11015			return_value_used = 1;
11016		} else if (TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))) {
11017			return_value_used = 0;
11018		} else {
11019			return_value_used = -1;
11020		}
11021	} else {
11022		return_value_used = -1;
11023	}
11024
11025	if (ZEND_OBSERVER_ENABLED) {
11026		if (Z_MODE(op1_addr) == IS_REG) {
11027			zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
11028
11029			if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) {
11030				return 0;
11031			}
11032			op1_addr = dst;
11033		}
11034		|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
11035		|	mov FCARG1x, FP
11036		|	SET_EX_OPLINE opline, REG0
11037		|	EXT_CALL zend_observer_fcall_end, REG0
11038	}
11039
11040	// if (!EX(return_value))
11041	if (Z_MODE(op1_addr) == IS_REG && Z_REG(op1_addr) == ZREG_REG1) {
11042		if (return_value_used != 0) {
11043			|	ldr REG2, EX->return_value
11044		}
11045		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, 0);
11046	} else {
11047		if (return_value_used != 0) {
11048			|	ldr REG1, EX->return_value
11049		}
11050		ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG1, 0);
11051	}
11052	if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
11053	    (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11054		if (return_value_used == -1) {
11055			|	cbz Rx(Z_REG(ret_addr)), >1
11056			|.cold_code
11057			|1:
11058		}
11059		if (return_value_used != 1) {
11060			if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11061				if (jit_return_label >= 0) {
11062					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, =>jit_return_label, ZREG_TMP1, ZREG_TMP2
11063				} else {
11064					|	IF_NOT_ZVAL_REFCOUNTED op1_addr, >9, ZREG_TMP1, ZREG_TMP2
11065				}
11066			}
11067			|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
11068			|	GC_DELREF FCARG1x, TMP1w
11069			if (RC_MAY_BE_1(op1_info)) {
11070				if (RC_MAY_BE_N(op1_info)) {
11071					if (jit_return_label >= 0) {
11072						|	bne =>jit_return_label
11073					} else {
11074						|	bne >9
11075					}
11076				}
11077				|	//SAVE_OPLINE()
11078				|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
11079				|	//????ldr REG1, EX->return_value // reload ???
11080			}
11081			if (return_value_used == -1) {
11082				if (jit_return_label >= 0) {
11083					|	b =>jit_return_label
11084				} else {
11085					|	b >9
11086				}
11087				|.code
11088			}
11089		}
11090	} else if (return_value_used == -1) {
11091		if (jit_return_label >= 0) {
11092			|	cbz Rx(Z_REG(ret_addr)), =>jit_return_label
11093		} else {
11094			|	cbz Rx(Z_REG(ret_addr)), >9
11095		}
11096	}
11097
11098	if (return_value_used == 0) {
11099		|9:
11100		return 1;
11101	}
11102
11103	if (opline->op1_type == IS_CONST) {
11104		zval *zv = RT_CONSTANT(opline, opline->op1);
11105		|	ZVAL_COPY_CONST ret_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
11106		if (Z_REFCOUNTED_P(zv)) {
11107			|	ADDREF_CONST zv, REG0, TMP1
11108		}
11109	} else if (opline->op1_type == IS_TMP_VAR) {
11110		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
11111	} else if (opline->op1_type == IS_CV) {
11112		if (op1_info & MAY_BE_REF) {
11113			|	LOAD_ZVAL_ADDR REG0, op1_addr
11114			|	ZVAL_DEREF REG0, op1_info, TMP1w
11115			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
11116		}
11117		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
11118		if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
11119			if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
11120			    (op1_info & (MAY_BE_REF|MAY_BE_OBJECT)) ||
11121			    !op_array->function_name) {
11122				|	TRY_ADDREF op1_info, REG0w, REG2, TMP1w
11123			} else if (return_value_used != 1) {
11124				|	// if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) ZVAL_NULL(retval_ptr);
11125				|	SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2
11126			}
11127		}
11128	} else {
11129		if (op1_info & MAY_BE_REF) {
11130			zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, offsetof(zend_reference, val));
11131
11132			|	IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1
11133			|.cold_code
11134			|1:
11135			|	// zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
11136			|	GET_ZVAL_PTR REG0, op1_addr, TMP1
11137			|	// ZVAL_COPY_VALUE(return_value, &ref->value);
11138			|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_REG2, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
11139			|	GC_DELREF REG0, TMP1w
11140			|	beq >2
11141			|	// if (IS_REFCOUNTED())
11142			if (jit_return_label >= 0) {
11143				|	IF_NOT_REFCOUNTED REG2w, =>jit_return_label, TMP1w
11144			} else {
11145				|	IF_NOT_REFCOUNTED REG2w, >9, TMP1w
11146			}
11147			|	// ADDREF
11148			|	GET_ZVAL_PTR REG2, ret_addr, TMP1 // reload
11149			|	GC_ADDREF REG2, TMP1w
11150			if (jit_return_label >= 0) {
11151				|	b =>jit_return_label
11152			} else {
11153				|	b >9
11154			}
11155			|2:
11156			|	mov FCARG1x, REG0
11157			|	EFREE_REFERENCE
11158			if (jit_return_label >= 0) {
11159				|	b =>jit_return_label
11160			} else {
11161				|	b >9
11162			}
11163			|.code
11164		}
11165		|	ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
11166	}
11167
11168	|9:
11169	return 1;
11170}
11171
11172static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, zend_jit_addr val_addr, zend_reg type_reg)
11173{
11174	ZEND_ASSERT(type_reg == ZREG_REG2);
11175
11176	|	GET_ZVAL_PTR REG1, val_addr, TMP1
11177	|	IF_NOT_REFCOUNTED REG2w, >2, TMP1w
11178	|	GET_LOW_8BITS TMP2w, REG2w
11179	|	IF_NOT_TYPE TMP2w, IS_REFERENCE, >1
11180	|	add REG1, REG1, #offsetof(zend_reference, val)
11181	|	GET_Z_TYPE_INFO REG2w, REG1
11182	|	GET_Z_PTR REG1, REG1
11183	|	IF_NOT_REFCOUNTED REG2w, >2, TMP1w
11184	|1:
11185	|	GC_ADDREF REG1, TMP2w
11186	|2:
11187	|	SET_ZVAL_PTR res_addr, REG1, TMP1
11188	|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
11189
11190	return 1;
11191}
11192
11193static int zend_jit_fetch_dim_read(dasm_State        **Dst,
11194                                   const zend_op      *opline,
11195                                   zend_ssa           *ssa,
11196                                   const zend_ssa_op  *ssa_op,
11197                                   uint32_t            op1_info,
11198                                   zend_jit_addr       op1_addr,
11199                                   bool                op1_avoid_refcounting,
11200                                   uint32_t            op2_info,
11201                                   uint32_t            res_info,
11202                                   zend_jit_addr       res_addr,
11203                                   uint8_t             dim_type)
11204{
11205	zend_jit_addr orig_op1_addr, op2_addr;
11206	const void *exit_addr = NULL;
11207	const void *not_found_exit_addr = NULL;
11208	const void *res_exit_addr = NULL;
11209	bool result_avoid_refcounting = 0;
11210	uint32_t may_be_string = (opline->opcode != ZEND_FETCH_LIST_R) ? MAY_BE_STRING : 0;
11211	int may_throw = 0;
11212
11213	orig_op1_addr = OP1_ADDR();
11214	op2_addr = OP2_ADDR();
11215
11216	if (opline->opcode != ZEND_FETCH_DIM_IS
11217	 && JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
11218		int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
11219		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
11220		if (!exit_addr) {
11221			return 0;
11222		}
11223	}
11224
11225	if ((res_info & MAY_BE_GUARD)
11226	 && JIT_G(current_frame)
11227	 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
11228		uint32_t flags = 0;
11229		uint32_t old_op1_info = 0;
11230		uint32_t old_info;
11231		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
11232		int32_t exit_point;
11233
11234		if (opline->opcode != ZEND_FETCH_LIST_R
11235		 && (opline->op1_type & (IS_VAR|IS_TMP_VAR))
11236		 && !op1_avoid_refcounting) {
11237			flags |= ZEND_JIT_EXIT_FREE_OP1;
11238		}
11239		if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
11240		 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11241			flags |= ZEND_JIT_EXIT_FREE_OP2;
11242		}
11243		if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
11244		 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
11245		 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
11246		 && (ssa_op+1)->op1_use == ssa_op->result_def
11247		 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))
11248		 && zend_jit_may_avoid_refcounting(opline+1, res_info)) {
11249			result_avoid_refcounting = 1;
11250			ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
11251		}
11252
11253		if (op1_avoid_refcounting) {
11254			old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
11255			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
11256		}
11257
11258		if (!(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))) {
11259			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
11260			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
11261			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
11262			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
11263			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
11264			res_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
11265			if (!res_exit_addr) {
11266				return 0;
11267			}
11268			res_info &= ~MAY_BE_GUARD;
11269			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
11270		}
11271
11272		if (opline->opcode == ZEND_FETCH_DIM_IS
11273		 && !(res_info & MAY_BE_NULL)) {
11274			old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
11275			SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_NULL, 0);
11276			SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_NULL);
11277			exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
11278			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
11279			not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
11280			if (!not_found_exit_addr) {
11281				return 0;
11282			}
11283		}
11284
11285		if (op1_avoid_refcounting) {
11286			SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
11287		}
11288	}
11289
11290	if (op1_info & MAY_BE_REF) {
11291		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
11292		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
11293		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
11294	}
11295
11296	if (op1_info & MAY_BE_ARRAY) {
11297		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
11298			if (exit_addr && !(op1_info & (MAY_BE_OBJECT|may_be_string))) {
11299				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, &exit_addr, ZREG_TMP1
11300			} else {
11301				|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
11302			}
11303		}
11304		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1
11305		if ((op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) ||
11306		    (opline->opcode != ZEND_FETCH_DIM_IS && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE)) {
11307			may_throw = 1;
11308		}
11309		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)) {
11310			return 0;
11311		}
11312	}
11313
11314	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
11315		if (op1_info & MAY_BE_ARRAY) {
11316			|.cold_code
11317			|7:
11318		}
11319
11320		if (opline->opcode != ZEND_FETCH_LIST_R && (op1_info & MAY_BE_STRING)) {
11321			may_throw = 1;
11322			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING))) {
11323				if (exit_addr && !(op1_info & MAY_BE_OBJECT)) {
11324					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &exit_addr, ZREG_TMP1
11325				} else {
11326					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1
11327				}
11328			}
11329			|	SET_EX_OPLINE opline, REG0
11330			|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1
11331			if (opline->opcode != ZEND_FETCH_DIM_IS) {
11332				if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) {
11333					|	GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1
11334					|	EXT_CALL zend_jit_fetch_dim_str_offset_r_helper, REG0
11335				} else {
11336					|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
11337					|	EXT_CALL zend_jit_fetch_dim_str_r_helper, REG0
11338				}
11339				|	SET_ZVAL_PTR res_addr, RETVALx, TMP1
11340				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING, TMP1w, TMP2
11341			} else {
11342				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
11343				|	LOAD_ZVAL_ADDR CARG3, res_addr
11344				|	EXT_CALL zend_jit_fetch_dim_str_is_helper, REG0
11345			}
11346			if ((op1_info & MAY_BE_ARRAY) ||
11347				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING)))) {
11348				|	b >9 // END
11349			}
11350			|6:
11351		}
11352
11353		if (op1_info & MAY_BE_OBJECT) {
11354			may_throw = 1;
11355			if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string))) {
11356				if (exit_addr) {
11357					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
11358				} else {
11359					|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >6, ZREG_TMP1
11360				}
11361			}
11362			|	SET_EX_OPLINE opline, REG0
11363		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
11364				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
11365		    }
11366			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
11367				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
11368				|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
11369			} else {
11370				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
11371			}
11372			|	LOAD_ZVAL_ADDR CARG3, res_addr
11373			if (opline->opcode != ZEND_FETCH_DIM_IS) {
11374				|	EXT_CALL zend_jit_fetch_dim_obj_r_helper, REG0
11375			} else {
11376				|	EXT_CALL zend_jit_fetch_dim_obj_is_helper, REG0
11377			}
11378			if ((op1_info & MAY_BE_ARRAY) ||
11379				(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) {
11380				|	b >9 // END
11381			}
11382			|6:
11383		}
11384
11385		if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))
11386		 && (!exit_addr || !(op1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) {
11387			if ((opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) || (op2_info & MAY_BE_UNDEF)) {
11388				|	SET_EX_OPLINE opline, REG0
11389				if (opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) {
11390					may_throw = 1;
11391					|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
11392					|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
11393					|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
11394					|	EXT_CALL zend_jit_undefined_op_helper, REG0
11395					|1:
11396				}
11397
11398				if (op2_info & MAY_BE_UNDEF) {
11399					may_throw = 1;
11400					|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1
11401					|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
11402					|	EXT_CALL zend_jit_undefined_op_helper, REG0
11403					|1:
11404				}
11405			}
11406
11407			if (opline->opcode != ZEND_FETCH_DIM_IS && opline->opcode != ZEND_FETCH_LIST_R) {
11408				may_throw = 1;
11409				if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
11410					|	LOAD_ZVAL_ADDR FCARG1x, orig_op1_addr
11411				} else {
11412					|	SET_EX_OPLINE opline, REG0
11413					if (Z_MODE(op1_addr) != IS_MEM_ZVAL ||
11414					    Z_REG(op1_addr) != ZREG_FCARG1 ||
11415					    Z_OFFSET(op1_addr) != 0) {
11416						|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
11417					}
11418				}
11419				|	EXT_CALL zend_jit_invalid_array_access, REG0
11420			}
11421			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
11422			if (op1_info & MAY_BE_ARRAY) {
11423				|	b >9 // END
11424			}
11425		}
11426
11427		if (op1_info & MAY_BE_ARRAY) {
11428			|.code
11429		}
11430	}
11431
11432	if (op1_info & MAY_BE_ARRAY) {
11433		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
11434
11435		|8:
11436		if (res_exit_addr) {
11437			uint32_t type = concrete_type(res_info);
11438			if ((op1_info & MAY_BE_ARRAY_OF_REF)
11439			 && dim_type != IS_UNKNOWN
11440			 && dim_type != IS_REFERENCE) {
11441				if (type < IS_STRING) {
11442					|	IF_NOT_ZVAL_TYPE val_addr, type, >1, ZREG_TMP1
11443					|.cold_code
11444					|1:
11445					|	IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, &res_exit_addr, ZREG_TMP1
11446					|	GET_Z_PTR REG0, REG0
11447					|	add REG0, REG0, #offsetof(zend_reference, val)
11448					|	IF_ZVAL_TYPE val_addr, type, >1, ZREG_TMP1
11449					|	b &res_exit_addr
11450					|.code
11451					|1:
11452				} else {
11453					|	GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1
11454					|	GET_LOW_8BITS TMP1w, REG2w
11455					|	IF_NOT_TYPE TMP1w, type, >1
11456					|.cold_code
11457					|1:
11458					|	IF_NOT_TYPE TMP1w, IS_REFERENCE, &res_exit_addr
11459					|	GET_Z_PTR REG0, REG0
11460					|	add REG0, REG0, #offsetof(zend_reference, val)
11461					|	GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1
11462					|	GET_LOW_8BITS TMP1w, REG2w
11463					|	IF_TYPE TMP1w, type, >1
11464					|	b &res_exit_addr
11465					|.code
11466					|1:
11467				}
11468			} else {
11469				if (op1_info & MAY_BE_ARRAY_OF_REF) {
11470					|	ZVAL_DEREF REG0, MAY_BE_REF, TMP1w
11471				}
11472				if (type < IS_STRING) {
11473					|	IF_NOT_ZVAL_TYPE val_addr, type, &res_exit_addr, ZREG_TMP1
11474				} else {
11475					|	GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1
11476					|	GET_LOW_8BITS TMP1w, REG2w
11477					|	IF_NOT_TYPE TMP1w, type, &res_exit_addr
11478				}
11479			}
11480			|	// ZVAL_COPY
11481			|7:
11482			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0
11483			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
11484				if (type < IS_STRING) {
11485					if (Z_REG(res_addr) != ZREG_FP ||
11486					    JIT_G(current_frame) == NULL ||
11487					    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
11488						|	SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2
11489					}
11490				} else {
11491					|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
11492					if (!result_avoid_refcounting) {
11493						|	TRY_ADDREF res_info, REG2w, REG1, TMP1w
11494					}
11495				}
11496			} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
11497				return 0;
11498			}
11499		} else if (op1_info & MAY_BE_ARRAY_OF_REF) {
11500			|	// ZVAL_COPY_DEREF
11501			|	GET_ZVAL_TYPE_INFO Rw(ZREG_REG2), val_addr, TMP1
11502			if (!zend_jit_zval_copy_deref(Dst, res_addr, val_addr, ZREG_REG2)) {
11503				return 0;
11504			}
11505		} else  {
11506			|	// ZVAL_COPY
11507			|	ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
11508			|	TRY_ADDREF res_info, REG1w, REG2, TMP1w
11509		}
11510	}
11511	|9: // END
11512
11513#ifdef ZEND_JIT_USE_RC_INFERENCE
11514	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
11515		/* Magic offsetGet() may increase refcount of the key */
11516		op2_info |= MAY_BE_RCN;
11517	}
11518#endif
11519
11520    if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
11521		if ((op2_info & MAY_HAVE_DTOR) && (op2_info & MAY_BE_RC1)) {
11522			may_throw = 1;
11523		}
11524		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
11525	}
11526	if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting) {
11527		if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
11528			if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
11529				may_throw = 1;
11530			}
11531			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
11532		}
11533	}
11534
11535	if (may_throw) {
11536		if (!zend_jit_check_exception(Dst)) {
11537			return 0;
11538		}
11539	}
11540
11541	return 1;
11542}
11543
11544static int zend_jit_fetch_dim(dasm_State    **Dst,
11545                              const zend_op  *opline,
11546                              uint32_t        op1_info,
11547                              zend_jit_addr   op1_addr,
11548                              uint32_t        op2_info,
11549                              zend_jit_addr   res_addr,
11550                              uint8_t         dim_type)
11551{
11552	zend_jit_addr op2_addr;
11553	int may_throw = 0;
11554
11555	op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
11556
11557	if (opline->opcode == ZEND_FETCH_DIM_RW) {
11558		|	SET_EX_OPLINE opline, REG0
11559	}
11560	if (op1_info & MAY_BE_REF) {
11561		may_throw = 1;
11562		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
11563		|	IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w
11564		|	GET_Z_PTR FCARG2x, FCARG1x
11565		|	ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))]
11566		|	cmp TMP1w, #IS_ARRAY
11567		|	bne >2
11568		|	add FCARG1x, FCARG2x, #offsetof(zend_reference, val)
11569		|	b >3
11570		|.cold_code
11571		|2:
11572		|	SET_EX_OPLINE opline, REG0
11573		if (opline->opcode != ZEND_FETCH_DIM_RW) {
11574			|	EXT_CALL zend_jit_prepare_assign_dim_ref, REG0
11575		}
11576		|	mov FCARG1x, RETVALx
11577		|	cbnz FCARG1x, >1
11578		|	b ->exception_handler_undef
11579		|.code
11580		|1:
11581		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
11582	}
11583
11584	if (op1_info & MAY_BE_ARRAY) {
11585		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
11586			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
11587		}
11588		|3:
11589		|	SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2
11590	}
11591	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) {
11592		if (op1_info & MAY_BE_ARRAY) {
11593			|.cold_code
11594			|7:
11595		}
11596		if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
11597			|	CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1
11598			|	bgt >7
11599		}
11600		if (Z_REG(op1_addr) != ZREG_FP) {
11601			|	str Rx(Z_REG(op1_addr)), T1 // save
11602		}
11603		if ((op1_info & MAY_BE_UNDEF)
11604		 && opline->opcode == ZEND_FETCH_DIM_RW) {
11605			may_throw = 1;
11606			if (op1_info & MAY_BE_NULL) {
11607				|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
11608			}
11609			|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
11610			|	EXT_CALL zend_jit_undefined_op_helper, REG0
11611			|1:
11612		}
11613		|	// ZVAL_ARR(container, zend_new_array(8));
11614		|	EXT_CALL _zend_new_array_0, REG0
11615		|	mov REG0, RETVALx
11616		if (Z_REG(op1_addr) != ZREG_FP) {
11617			|	ldr Rx(Z_REG(op1_addr)), T1 // restore
11618		}
11619		|	SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1
11620		|	SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2
11621		|	mov FCARG1x, REG0
11622		if (op1_info & MAY_BE_ARRAY) {
11623			|	b >1
11624			|.code
11625			|1:
11626		}
11627	}
11628
11629	if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
11630		|6:
11631		if (opline->op2_type == IS_UNUSED) {
11632			may_throw = 1;
11633			|	// var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
11634			|	LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
11635			|	EXT_CALL zend_hash_next_index_insert, REG0
11636			|	// if (UNEXPECTED(!var_ptr)) {
11637			|	cbz RETVALx, >1
11638			|.cold_code
11639			|1:
11640			|	// zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
11641			|	CANNOT_ADD_ELEMENT opline
11642			|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF, TMP1w, TMP2
11643			|	//ZEND_VM_C_GOTO(assign_dim_op_ret_null);
11644			|	b >8
11645			|.code
11646			|	SET_ZVAL_PTR res_addr, RETVALx, TMP1
11647			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2
11648		} else {
11649			uint32_t type;
11650
11651			switch (opline->opcode) {
11652				case ZEND_FETCH_DIM_W:
11653				case ZEND_FETCH_LIST_W:
11654					type = BP_VAR_W;
11655					break;
11656				case ZEND_FETCH_DIM_RW:
11657					may_throw = 1;
11658					type = BP_VAR_RW;
11659					break;
11660				case ZEND_FETCH_DIM_UNSET:
11661					type = BP_VAR_UNSET;
11662					break;
11663				default:
11664					ZEND_UNREACHABLE();
11665			}
11666
11667			if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) {
11668				may_throw = 1;
11669			}
11670			if (!zend_jit_fetch_dimension_address_inner(Dst, opline, type, op1_info, op2_info, dim_type, NULL, NULL, NULL)) {
11671				return 0;
11672			}
11673
11674			|8:
11675			|	SET_ZVAL_PTR res_addr, REG0, TMP1
11676			|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2
11677
11678			if (type == BP_VAR_RW || (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
11679				|.cold_code
11680				|9:
11681				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
11682				|	b >8
11683				|.code
11684			}
11685		}
11686	}
11687
11688	if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) {
11689		may_throw = 1;
11690		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
11691			|.cold_code
11692			|7:
11693		}
11694
11695		if (opline->opcode != ZEND_FETCH_DIM_RW) {
11696			|	SET_EX_OPLINE opline, REG0
11697		}
11698		if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
11699			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
11700		}
11701	    if (opline->op2_type == IS_UNUSED) {
11702			|	mov FCARG2x, xzr
11703		} else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
11704			ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
11705			|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
11706		} else {
11707			|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
11708		}
11709		|	LOAD_ZVAL_ADDR CARG3, res_addr
11710		switch (opline->opcode) {
11711			case ZEND_FETCH_DIM_W:
11712			case ZEND_FETCH_LIST_W:
11713				|	EXT_CALL zend_jit_fetch_dim_obj_w_helper, REG0
11714				break;
11715			case ZEND_FETCH_DIM_RW:
11716				|	EXT_CALL zend_jit_fetch_dim_obj_rw_helper, REG0
11717				break;
11718//			case ZEND_FETCH_DIM_UNSET:
11719//				|	EXT_CALL zend_jit_fetch_dim_obj_unset_helper, REG0
11720//				break;
11721			default:
11722				ZEND_UNREACHABLE();
11723		}
11724
11725		if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) {
11726			|	b >8 // END
11727			|.code
11728		}
11729	}
11730
11731#ifdef ZEND_JIT_USE_RC_INFERENCE
11732	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))) {
11733		/* ASSIGN_DIM may increase refcount of the key */
11734		op2_info |= MAY_BE_RCN;
11735	}
11736#endif
11737
11738	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
11739	 && (op2_info & MAY_HAVE_DTOR)
11740	 && (op2_info & MAY_BE_RC1)) {
11741		may_throw = 1;
11742	}
11743	|8:
11744	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
11745
11746	if (may_throw) {
11747		if (!zend_jit_check_exception(Dst)) {
11748			return 0;
11749		}
11750	}
11751	return 1;
11752}
11753
11754static int zend_jit_isset_isempty_dim(dasm_State    **Dst,
11755                                      const zend_op  *opline,
11756                                      uint32_t        op1_info,
11757                                      zend_jit_addr   op1_addr,
11758                                      bool            op1_avoid_refcounting,
11759                                      uint32_t        op2_info,
11760                                      uint8_t         dim_type,
11761                                      int             may_throw,
11762                                      zend_uchar      smart_branch_opcode,
11763                                      uint32_t        target_label,
11764                                      uint32_t        target_label2,
11765                                      const void     *exit_addr)
11766{
11767	zend_jit_addr op2_addr, res_addr;
11768
11769	// TODO: support for empty() ???
11770	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
11771
11772	op2_addr = OP2_ADDR();
11773	res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
11774
11775	if (op1_info & MAY_BE_REF) {
11776		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
11777		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
11778		op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
11779	}
11780
11781	if (op1_info & MAY_BE_ARRAY) {
11782		const void *found_exit_addr = NULL;
11783		const void *not_found_exit_addr = NULL;
11784
11785		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
11786			|	IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
11787		}
11788		|	GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1
11789		if (exit_addr
11790		 && !(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY))
11791		 && !may_throw
11792		 && (!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || op1_avoid_refcounting)
11793		 && (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)))) {
11794			if (smart_branch_opcode == ZEND_JMPNZ) {
11795				found_exit_addr = exit_addr;
11796			} else {
11797				not_found_exit_addr = exit_addr;
11798			}
11799		}
11800		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)) {
11801			return 0;
11802		}
11803
11804		if (found_exit_addr) {
11805			|9:
11806			return 1;
11807		} else if (not_found_exit_addr) {
11808			|8:
11809			return 1;
11810		}
11811	}
11812
11813	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) {
11814		if (op1_info & MAY_BE_ARRAY) {
11815			|.cold_code
11816			|7:
11817		}
11818
11819		if (op1_info & (MAY_BE_STRING|MAY_BE_OBJECT)) {
11820			|	SET_EX_OPLINE opline, REG0
11821		    if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
11822				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
11823			}
11824			if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
11825				ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
11826				|	LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1)
11827			} else {
11828				|	LOAD_ZVAL_ADDR FCARG2x, op2_addr
11829			}
11830			|	EXT_CALL zend_jit_isset_dim_helper, REG0
11831			|	cbz RETVALw, >9
11832			if (op1_info & MAY_BE_ARRAY) {
11833				|	b >8
11834				|.code
11835			}
11836		} else {
11837			if (op2_info & MAY_BE_UNDEF) {
11838				if (op2_info & MAY_BE_ANY) {
11839					|	IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1
11840				}
11841				|	SET_EX_OPLINE opline, REG0
11842				|	LOAD_32BIT_VAL FCARG1w, opline->op2.var
11843				|	EXT_CALL zend_jit_undefined_op_helper, REG0
11844				|1:
11845			}
11846			if (op1_info & MAY_BE_ARRAY) {
11847				|	b >9
11848				|.code
11849			}
11850		}
11851	}
11852
11853#ifdef ZEND_JIT_USE_RC_INFERENCE
11854	if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) {
11855		/* Magic offsetExists() may increase refcount of the key */
11856		op2_info |= MAY_BE_RCN;
11857	}
11858#endif
11859
11860	if (op1_info & (MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_OBJECT)) {
11861		|8:
11862		|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
11863		if (!op1_avoid_refcounting) {
11864			|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
11865		}
11866		if (may_throw) {
11867			if (!zend_jit_check_exception_undef_result(Dst, opline)) {
11868				return 0;
11869			}
11870		}
11871		if (!(opline->extended_value & ZEND_ISEMPTY)) {
11872			if (exit_addr) {
11873				if (smart_branch_opcode == ZEND_JMPNZ) {
11874					|	b &exit_addr
11875				} else {
11876					|	b >8
11877				}
11878			} else if (smart_branch_opcode) {
11879				if (smart_branch_opcode == ZEND_JMPZ) {
11880					|	b =>target_label2
11881				} else if (smart_branch_opcode == ZEND_JMPNZ) {
11882					|	b =>target_label
11883				} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11884					|	b =>target_label2
11885				} else {
11886					ZEND_UNREACHABLE();
11887				}
11888			} else {
11889				|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
11890				|	b >8
11891			}
11892		} else {
11893			|	NIY // TODO: support for empty()
11894		}
11895	}
11896
11897	|9: // not found
11898	|	FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2
11899	if (!op1_avoid_refcounting) {
11900		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
11901	}
11902	if (may_throw) {
11903		if (!zend_jit_check_exception_undef_result(Dst, opline)) {
11904			return 0;
11905		}
11906	}
11907	if (!(opline->extended_value & ZEND_ISEMPTY)) {
11908		if (exit_addr) {
11909			if (smart_branch_opcode == ZEND_JMPZ) {
11910				|	b &exit_addr
11911			}
11912		} else if (smart_branch_opcode) {
11913			if (smart_branch_opcode == ZEND_JMPZ) {
11914				|	b =>target_label
11915			} else if (smart_branch_opcode == ZEND_JMPNZ) {
11916			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
11917				|	b =>target_label
11918			} else {
11919				ZEND_UNREACHABLE();
11920			}
11921		} else {
11922			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
11923		}
11924	} else {
11925		|	NIY // TODO: support for empty()
11926	}
11927
11928	|8:
11929
11930	return 1;
11931}
11932
11933static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
11934{
11935	zend_jit_addr op1_addr = OP1_ADDR();
11936	zend_string *varname = Z_STR_P(RT_CONSTANT(opline, opline->op2));
11937
11938	|	// idx = (uint32_t)(uintptr_t)CACHED_PTR(opline->extended_value) - 1;
11939	|	ldr FCARG2x, EX->run_time_cache
11940	|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, FCARG2x, opline->extended_value, TMP1
11941	|	sub REG0, REG0, #1
11942	|	// if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket)))
11943	|	MEM_LOAD_32_ZTS ldr, REG1w, executor_globals, symbol_table.nNumUsed, REG1
11944	|	cmp REG0, REG1, lsl #5
11945	|	bhs >9
11946	|	// Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx);
11947	|	MEM_LOAD_64_ZTS ldr, TMP1, executor_globals, symbol_table.arData, REG1
11948	|	add REG0, REG0, TMP1
11949	|	IF_NOT_Z_TYPE REG0, IS_REFERENCE, >9, TMP1w
11950	|	// (EXPECTED(p->key == varname))
11951	|	ldr TMP1, [REG0, #offsetof(Bucket, key)]
11952	|	LOAD_ADDR TMP2, varname
11953	|	cmp TMP1, TMP2
11954	|	bne >9
11955	|	GET_Z_PTR REG0, REG0
11956	|	GC_ADDREF REG0, TMP1w
11957	|1:
11958	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
11959		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11960			|	// if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr)))
11961			|	IF_ZVAL_REFCOUNTED op1_addr, >2, ZREG_TMP1, ZREG_TMP2
11962			|.cold_code
11963			|2:
11964		}
11965		|	// zend_refcounted *garbage = Z_COUNTED_P(variable_ptr);
11966		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
11967		|	// ZVAL_REF(variable_ptr, ref)
11968		|	SET_ZVAL_PTR op1_addr, REG0, TMP1
11969		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2
11970		|	// if (GC_DELREF(garbage) == 0)
11971		|	GC_DELREF FCARG1x, TMP1w
11972		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
11973			|	bne >3
11974		} else {
11975			|	bne >5
11976		}
11977		|	ZVAL_DTOR_FUNC op1_info, opline, TMP1
11978		|	b >5
11979		if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
11980			|3:
11981			|	// GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr)
11982			|	IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w
11983			|	EXT_CALL gc_possible_root, REG0
11984			|	b >5
11985		}
11986		if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11987			|.code
11988		}
11989	}
11990
11991	if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
11992		|	// ZVAL_REF(variable_ptr, ref)
11993		|	SET_ZVAL_PTR op1_addr, REG0, TMP1
11994		|	SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2
11995	}
11996	|5:
11997	//END of handler
11998
11999	|.cold_code
12000	|9:
12001	|	LOAD_ADDR FCARG1x, (ptrdiff_t)varname
12002	if (opline->extended_value) {
12003		|	ADD_SUB_64_WITH_CONST_32 add, FCARG2x, FCARG2x, opline->extended_value, TMP1
12004	}
12005	|	EXT_CALL zend_jit_fetch_global_helper, REG0
12006	|	mov REG0, RETVALx
12007	|	b <1
12008	|.code
12009
12010	return 1;
12011}
12012
12013static int zend_jit_verify_arg_type(dasm_State **Dst, const zend_op *opline, zend_arg_info *arg_info, bool check_exception)
12014{
12015	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12016	bool in_cold = 0;
12017	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
12018	zend_reg tmp_reg = (type_mask == 0 || is_power_of_two(type_mask)) ? ZREG_FCARG1 : ZREG_REG0;
12019
12020	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
12021	 && JIT_G(current_frame)
12022	 && JIT_G(current_frame)->prev) {
12023		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
12024		uint8_t type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var));
12025
12026		if (type != IS_UNKNOWN && (type_mask & (1u << type))) {
12027			return 1;
12028		}
12029	}
12030
12031	if (ZEND_ARG_SEND_MODE(arg_info)) {
12032		if (opline->opcode == ZEND_RECV_INIT) {
12033			|	LOAD_ZVAL_ADDR Rx(tmp_reg), res_addr
12034			|	ZVAL_DEREF Rx(tmp_reg), MAY_BE_REF, TMP1w
12035			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, 0);
12036		} else {
12037			|	GET_ZVAL_PTR Rx(tmp_reg), res_addr, TMP1
12038			res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, offsetof(zend_reference, val));
12039		}
12040	}
12041
12042	if (type_mask != 0) {
12043		if (is_power_of_two(type_mask)) {
12044			uint32_t type_code = concrete_type(type_mask);
12045			|	IF_NOT_ZVAL_TYPE res_addr, type_code, >1, ZREG_TMP1
12046		} else {
12047			|	mov REG2w, #1
12048			|	MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, Rx(Z_REG(res_addr)), Z_OFFSET(res_addr)+offsetof(zval, u1.v.type), TMP1
12049			|	lsl REG2w, REG2w, REG1w
12050			|	TST_32_WITH_CONST REG2w, type_mask, TMP1w
12051			|	beq >1
12052		}
12053
12054		|.cold_code
12055		|1:
12056
12057		in_cold = 1;
12058	}
12059
12060	if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) {
12061		|	LOAD_ZVAL_ADDR FCARG1x, res_addr
12062	}
12063	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12064		|	SET_EX_OPLINE opline, REG0
12065	} else {
12066		|	ADDR_STORE EX->opline, opline, REG0
12067	}
12068	|	LOAD_ADDR FCARG2x, (ptrdiff_t)arg_info
12069	|	EXT_CALL zend_jit_verify_arg_slow, REG0
12070
12071	if (check_exception) {
12072		|	GET_LOW_8BITS REG0w, RETVALw
12073		if (in_cold) {
12074			|	cbnz REG0w, >1
12075			|	b ->exception_handler
12076			|.code
12077			|1:
12078		} else {
12079			|	cbz REG0w, ->exception_handler
12080		}
12081	} else if (in_cold) {
12082		|	b >1
12083		|.code
12084		|1:
12085	}
12086
12087	return 1;
12088}
12089
12090static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array)
12091{
12092	uint32_t arg_num = opline->op1.num;
12093	zend_arg_info *arg_info = NULL;
12094
12095	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
12096		if (EXPECTED(arg_num <= op_array->num_args)) {
12097			arg_info = &op_array->arg_info[arg_num-1];
12098		} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
12099			arg_info = &op_array->arg_info[op_array->num_args];
12100		}
12101		if (arg_info && !ZEND_TYPE_IS_SET(arg_info->type)) {
12102			arg_info = NULL;
12103		}
12104	}
12105
12106	if (arg_info || (opline+1)->opcode != ZEND_RECV) {
12107		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12108			if (!JIT_G(current_frame) ||
12109			    TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) < 0 ||
12110			    arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) {
12111				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12112				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12113
12114				if (!exit_addr) {
12115					return 0;
12116				}
12117				|	ldr TMP1w, EX->This.u2.num_args
12118				|	CMP_32_WITH_CONST TMP1w, arg_num, TMP2w
12119				|	blo &exit_addr
12120			}
12121		} else {
12122			|	ldr TMP1w, EX->This.u2.num_args
12123			|	CMP_32_WITH_CONST TMP1w, arg_num, TMP2w
12124			|	blo >1
12125			|.cold_code
12126			|1:
12127			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12128				|	SET_EX_OPLINE opline, REG0
12129			} else {
12130				|	ADDR_STORE EX->opline, opline, REG0
12131			}
12132			|	mov FCARG1x, FP
12133			|	EXT_CALL zend_missing_arg_error, REG0
12134			|	b ->exception_handler
12135			|.code
12136		}
12137	}
12138
12139	if (arg_info) {
12140		if (!zend_jit_verify_arg_type(Dst, opline, arg_info, 1)) {
12141			return 0;
12142		}
12143	}
12144
12145	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
12146		if ((opline+1)->opcode != ZEND_RECV && (opline+1)->opcode != ZEND_RECV_INIT) {
12147			|	LOAD_IP_ADDR (opline + 1)
12148			zend_jit_set_last_valid_opline(opline + 1);
12149		}
12150	}
12151
12152	return 1;
12153}
12154
12155static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool is_last, int may_throw)
12156{
12157	uint32_t arg_num = opline->op1.num;
12158	zval *zv = RT_CONSTANT(opline, opline->op2);
12159	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12160
12161	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
12162	 && JIT_G(current_frame)
12163	 && TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) >= 0) {
12164		if (arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) {
12165			|	ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
12166			if (Z_REFCOUNTED_P(zv)) {
12167				|	ADDREF_CONST zv, REG0, TMP1
12168			}
12169		}
12170	} else {
12171		if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
12172		    (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
12173			|	ldr TMP1w, EX->This.u2.num_args
12174			|	CMP_32_WITH_CONST TMP1w, arg_num, TMP2w
12175			|	bhs >5
12176		}
12177		|	ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
12178		if (Z_REFCOUNTED_P(zv)) {
12179			|	ADDREF_CONST zv, REG0, TMP1
12180		}
12181	}
12182
12183	if (Z_CONSTANT_P(zv)) {
12184		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12185			|	SET_EX_OPLINE opline, REG0
12186		} else {
12187			|	ADDR_STORE EX->opline, opline, REG0
12188		}
12189		|	LOAD_ZVAL_ADDR FCARG1x, res_addr
12190		|	ldr REG0, EX->func
12191		|	ldr FCARG2x, [REG0, #offsetof(zend_op_array, scope)]
12192		|	EXT_CALL zval_update_constant_ex, REG0
12193		|	cbnz RETVALw, >1
12194		|.cold_code
12195		|1:
12196		|	ZVAL_PTR_DTOR res_addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 1, 0, opline, ZREG_TMP1, ZREG_TMP2
12197		|	SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF, TMP1w, TMP2
12198		|	b ->exception_handler
12199		|.code
12200	}
12201
12202	|5:
12203
12204	if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
12205		do {
12206			zend_arg_info *arg_info;
12207
12208			if (arg_num <= op_array->num_args) {
12209				arg_info = &op_array->arg_info[arg_num-1];
12210			} else if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
12211				arg_info = &op_array->arg_info[op_array->num_args];
12212			} else {
12213				break;
12214			}
12215			if (!ZEND_TYPE_IS_SET(arg_info->type)) {
12216				break;
12217			}
12218			if (!zend_jit_verify_arg_type(Dst, opline, arg_info, may_throw)) {
12219				return 0;
12220			}
12221		} while (0);
12222	}
12223
12224	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
12225		if (is_last) {
12226			|	LOAD_IP_ADDR (opline + 1)
12227			zend_jit_set_last_valid_opline(opline + 1);
12228		}
12229	}
12230	return 1;
12231}
12232
12233static int zend_jit_class_guard(dasm_State **Dst, const zend_op *opline, zend_class_entry *ce)
12234{
12235	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
12236	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12237
12238	if (!exit_addr) {
12239		return 0;
12240	}
12241
12242	|	LOAD_ADDR TMP1, ((ptrdiff_t)ce)
12243	|	ldr TMP2, [FCARG1x, #offsetof(zend_object, ce)]
12244	|	cmp TMP2, TMP1
12245	|	bne &exit_addr
12246
12247	return 1;
12248}
12249
12250static int zend_jit_fetch_obj(dasm_State          **Dst,
12251                              const zend_op        *opline,
12252                              const zend_op_array  *op_array,
12253                              zend_ssa             *ssa,
12254                              const zend_ssa_op    *ssa_op,
12255                              uint32_t              op1_info,
12256                              zend_jit_addr         op1_addr,
12257                              bool                  op1_indirect,
12258                              zend_class_entry     *ce,
12259                              bool                  ce_is_instanceof,
12260                              bool                  on_this,
12261                              bool                  delayed_fetch_this,
12262                              bool                  op1_avoid_refcounting,
12263                              zend_class_entry     *trace_ce,
12264                              uint8_t               prop_type,
12265                              int                   may_throw)
12266{
12267	zval *member;
12268	zend_property_info *prop_info;
12269	bool may_be_dynamic = 1;
12270	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12271	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
12272	zend_jit_addr prop_addr;
12273	uint32_t res_info = RES_INFO();
12274	bool type_loaded = 0;
12275
12276	ZEND_ASSERT(opline->op2_type == IS_CONST);
12277	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
12278
12279	member = RT_CONSTANT(opline, opline->op2);
12280	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
12281	prop_info = zend_get_known_property_info(op_array, ce, Z_STR_P(member), on_this, op_array->filename);
12282
12283	if (on_this) {
12284		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
12285	} else {
12286		if (opline->op1_type == IS_VAR
12287		 && opline->opcode == ZEND_FETCH_OBJ_W
12288		 && (op1_info & MAY_BE_INDIRECT)
12289		 && Z_REG(op1_addr) == ZREG_FP) {
12290			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
12291			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
12292			|	GET_Z_PTR FCARG1x, FCARG1x
12293			|1:
12294			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12295		}
12296		if (op1_info & MAY_BE_REF) {
12297			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12298				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
12299			}
12300			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
12301			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12302		}
12303		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
12304			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12305				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12306				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12307
12308				if (!exit_addr) {
12309					return 0;
12310				}
12311				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
12312			} else {
12313				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >7, ZREG_TMP1
12314			}
12315		}
12316		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
12317	}
12318
12319	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
12320		prop_info = zend_get_known_property_info(op_array, trace_ce, Z_STR_P(member), on_this, op_array->filename);
12321		if (prop_info) {
12322			ce = trace_ce;
12323			ce_is_instanceof = 0;
12324			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
12325				if (on_this && JIT_G(current_frame)
12326				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
12327					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
12328				} else if (zend_jit_class_guard(Dst, opline, ce)) {
12329					if (on_this && JIT_G(current_frame)) {
12330						JIT_G(current_frame)->ce = ce;
12331						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
12332					}
12333				} else {
12334					return 0;
12335				}
12336				if (ssa->var_info && ssa_op->op1_use >= 0) {
12337					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
12338					ssa->var_info[ssa_op->op1_use].ce = ce;
12339					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
12340				}
12341			}
12342		}
12343	}
12344
12345	if (!prop_info) {
12346		|	ldr REG0, EX->run_time_cache
12347		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS), TMP1
12348		|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
12349		|	cmp REG2, TMP1
12350		|	bne >5
12351		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, ((opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS)  + sizeof(void*)), TMP1
12352		may_be_dynamic = zend_may_be_dynamic_property(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
12353		if (may_be_dynamic) {
12354			|	tst REG0, REG0
12355			if (opline->opcode == ZEND_FETCH_OBJ_W) {
12356				|	blt >5
12357			} else {
12358				|	blt >8 // dynamic property
12359			}
12360		}
12361		|	add TMP1, FCARG1x, REG0
12362		|	ldr REG2w, [TMP1, #offsetof(zval,u1.type_info)]
12363		|	IF_UNDEF REG2w, >5
12364		|	mov FCARG1x, TMP1
12365		type_loaded = 1;
12366		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12367		if (opline->opcode == ZEND_FETCH_OBJ_W
12368		 && (!ce ||	ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT)))) {
12369			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
12370
12371			|	ldr REG0, EX->run_time_cache
12372			|	MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, ((opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS)  + sizeof(void*) * 2), TMP1
12373			|	cbnz FCARG2x, >1
12374			|.cold_code
12375			|1:
12376			|	ldr TMP1w, [FCARG2x, #offsetof(zend_property_info, flags)]
12377			|	tst TMP1w, #ZEND_ACC_READONLY
12378			if (flags) {
12379				|	beq >3
12380			} else {
12381				|	beq >4
12382			}
12383			|	IF_NOT_TYPE REG2w, IS_OBJECT_EX, >2
12384			|	GET_Z_PTR REG2, FCARG1x
12385			|	GC_ADDREF REG2, TMP1w
12386			|	SET_ZVAL_PTR res_addr, REG2, TMP1
12387			|	SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX, TMP1w, TMP2
12388			|	b >9
12389			|2:
12390			|	mov FCARG1x, FCARG2x
12391			|	SET_EX_OPLINE opline, REG0
12392			|	EXT_CALL zend_readonly_property_modification_error, REG0
12393			|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR, TMP1w, TMP2
12394			|	b >9
12395			|3:
12396			if (flags == ZEND_FETCH_DIM_WRITE) {
12397				|	SET_EX_OPLINE opline, REG0
12398				|	EXT_CALL zend_jit_check_array_promotion, REG0
12399				|	b >9
12400			} else if (flags == ZEND_FETCH_REF) {
12401				|	LOAD_ZVAL_ADDR CARG3, res_addr
12402				|	EXT_CALL zend_jit_create_typed_ref, REG0
12403				|	b >9
12404			} else {
12405				ZEND_ASSERT(flags == 0);
12406			}
12407			|.code
12408			|4:
12409		}
12410	} else {
12411		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
12412		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12413			if (opline->opcode == ZEND_FETCH_OBJ_W || !(res_info & MAY_BE_GUARD) || !JIT_G(current_frame)) {
12414				/* perform IS_UNDEF check only after result type guard (during deoptimization) */
12415				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12416				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12417
12418				if (!exit_addr) {
12419					return 0;
12420				}
12421				type_loaded = 1;
12422				|	MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1
12423				|	IF_UNDEF REG2w, &exit_addr
12424			}
12425		} else {
12426			type_loaded = 1;
12427			|	MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1
12428			|	IF_UNDEF REG2w, >5
12429		}
12430		if (opline->opcode == ZEND_FETCH_OBJ_W && (prop_info->flags & ZEND_ACC_READONLY)) {
12431			if (!type_loaded) {
12432				type_loaded = 1;
12433				|	MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1
12434			}
12435			|	IF_NOT_TYPE REG2w, IS_OBJECT_EX, >4
12436			|	GET_ZVAL_PTR REG2, prop_addr, TMP1
12437			|	GC_ADDREF REG2, TMP1w
12438			|	SET_ZVAL_PTR res_addr, REG2, TMP1
12439			|	SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX, TMP1w, TMP2
12440			|	b >9
12441			|.cold_code
12442			|4:
12443			|	LOAD_ADDR FCARG1x, prop_info
12444			|	SET_EX_OPLINE opline, REG0
12445			|	EXT_CALL zend_readonly_property_modification_error, REG0
12446			|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR, TMP1w, TMP2
12447			|	b >9
12448			|.code
12449		}
12450		if (opline->opcode == ZEND_FETCH_OBJ_W
12451		 && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS)
12452		 && ZEND_TYPE_IS_SET(prop_info->type)) {
12453			uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
12454
12455			if (flags == ZEND_FETCH_DIM_WRITE) {
12456				if ((ZEND_TYPE_FULL_MASK(prop_info->type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) == 0) {
12457					if (!type_loaded) {
12458						type_loaded = 1;
12459						|	MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1
12460					}
12461					|	cmp REG2w, #IS_FALSE
12462					|	ble >1
12463					|.cold_code
12464					|1:
12465					if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
12466						|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
12467					}
12468					|	LOAD_ADDR FCARG2x, prop_info
12469					|	SET_EX_OPLINE opline, REG0
12470					|	EXT_CALL zend_jit_check_array_promotion, REG0
12471					|	b >9
12472					|.code
12473				}
12474			} else if (flags == ZEND_FETCH_REF) {
12475				if (!type_loaded) {
12476					type_loaded = 1;
12477					|	MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1
12478				}
12479				|	GET_LOW_8BITS TMP1w, REG2w
12480				|	IF_TYPE TMP1w, IS_REFERENCE, >1
12481				if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
12482					|	LOAD_ADDR FCARG2x, prop_info
12483				} else {
12484					int prop_info_offset =
12485						(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
12486
12487					|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
12488					|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
12489					|	MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
12490				}
12491				if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
12492					|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
12493				}
12494				|	LOAD_ZVAL_ADDR CARG3, res_addr
12495				|	EXT_CALL zend_jit_create_typed_ref, REG0
12496				|	b >9
12497				|1:
12498			} else {
12499				ZEND_UNREACHABLE();
12500			}
12501		}
12502	}
12503	if (opline->opcode == ZEND_FETCH_OBJ_W) {
12504		if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
12505			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
12506		}
12507		|	SET_ZVAL_PTR res_addr, FCARG1x, TMP1
12508		|	SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2
12509		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && prop_info) {
12510			ssa->var_info[ssa_op->result_def].indirect_reference = 1;
12511		}
12512	} else {
12513		bool result_avoid_refcounting = 0;
12514
12515		if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) {
12516			uint32_t flags = 0;
12517			uint32_t old_info;
12518			zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
12519			int32_t exit_point;
12520			const void *exit_addr;
12521			uint32_t type;
12522			zend_jit_addr val_addr = prop_addr;
12523
12524			if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
12525			 && !delayed_fetch_this
12526			 && !op1_avoid_refcounting) {
12527				flags = ZEND_JIT_EXIT_FREE_OP1;
12528			}
12529
12530			if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
12531			 && !(flags & ZEND_JIT_EXIT_FREE_OP1)
12532			 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
12533			 && (ssa_op+1)->op1_use == ssa_op->result_def
12534			 && zend_jit_may_avoid_refcounting(opline+1, res_info)) {
12535				result_avoid_refcounting = 1;
12536				ssa->var_info[ssa_op->result_def].avoid_refcounting = 1;
12537			}
12538
12539			type = concrete_type(res_info);
12540
12541			if (prop_type != IS_UNKNOWN
12542			 && prop_type != IS_UNDEF
12543			 && prop_type != IS_REFERENCE
12544			 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT) {
12545				exit_point = zend_jit_trace_get_exit_point(opline, 0);
12546				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12547				if (!exit_addr) {
12548					return 0;
12549				}
12550			} else {
12551				val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
12552				|	LOAD_ZVAL_ADDR REG0, prop_addr
12553				if (op1_avoid_refcounting) {
12554					SET_STACK_REG(JIT_G(current_frame)->stack,
12555						EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
12556				}
12557				old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
12558				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
12559				SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
12560				exit_point = zend_jit_trace_get_exit_point(opline+1, flags);
12561				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
12562				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12563				if (!exit_addr) {
12564					return 0;
12565				}
12566
12567				if (!type_loaded) {
12568					type_loaded = 1;
12569					|	MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1
12570				}
12571				|	// ZVAL_DEREF()
12572				|	GET_LOW_8BITS TMP1w, REG2w
12573				|	IF_NOT_TYPE TMP1w, IS_REFERENCE, >1
12574				|	GET_Z_PTR REG0, REG0
12575				|	add REG0, REG0, #offsetof(zend_reference, val)
12576				|	GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1
12577			}
12578			res_info &= ~MAY_BE_GUARD;
12579			ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
12580			if (type < IS_STRING) {
12581				|1:
12582				if (type_loaded) {
12583					|	IF_NOT_TYPE REG2w, type, &exit_addr
12584				} else {
12585					|	IF_NOT_ZVAL_TYPE val_addr, type, &exit_addr, ZREG_TMP1
12586				}
12587			} else {
12588				if (!type_loaded) {
12589					type_loaded = 1;
12590					|	GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1
12591				}
12592				|1:
12593				|	GET_LOW_8BITS TMP1w, REG2w
12594				|	IF_NOT_TYPE TMP1w, type, &exit_addr
12595			}
12596			|	// ZVAL_COPY
12597			|	ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0
12598			if (type < IS_STRING) {
12599				if (Z_REG(res_addr) != ZREG_FP ||
12600				    JIT_G(current_frame) == NULL ||
12601				    STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) {
12602					|	SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2
12603				}
12604			} else {
12605				|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
12606				if (!result_avoid_refcounting) {
12607					|	TRY_ADDREF res_info, REG2w, REG1, TMP1w
12608				}
12609			}
12610		} else {
12611			if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_REG2)) {
12612				return 0;
12613			}
12614		}
12615	}
12616
12617	if (op1_avoid_refcounting) {
12618		SET_STACK_REG(JIT_G(current_frame)->stack,
12619			EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
12620	}
12621
12622	|.cold_code
12623
12624	if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || !prop_info) {
12625		|5:
12626		|	SET_EX_OPLINE opline, REG0
12627		if (opline->opcode == ZEND_FETCH_OBJ_W) {
12628			|	EXT_CALL zend_jit_fetch_obj_w_slow, REG0
12629		} else if (opline->opcode != ZEND_FETCH_OBJ_IS) {
12630			|	EXT_CALL zend_jit_fetch_obj_r_slow, REG0
12631		} else {
12632			|	EXT_CALL zend_jit_fetch_obj_is_slow, REG0
12633		}
12634		|	b >9
12635	}
12636
12637	if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)- MAY_BE_OBJECT)) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
12638		|7:
12639		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
12640			|	SET_EX_OPLINE opline, REG0
12641			if (opline->opcode != ZEND_FETCH_OBJ_W
12642			 && (op1_info & MAY_BE_UNDEF)) {
12643				zend_jit_addr orig_op1_addr = OP1_ADDR();
12644
12645				if (op1_info & MAY_BE_ANY) {
12646					|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1
12647				}
12648				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
12649				|	EXT_CALL zend_jit_undefined_op_helper, REG0
12650				|1:
12651				|	LOAD_ZVAL_ADDR FCARG1x, orig_op1_addr
12652			} else if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12653				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
12654			}
12655			|	LOAD_ADDR FCARG2x, Z_STRVAL_P(member)
12656			if (opline->opcode == ZEND_FETCH_OBJ_W) {
12657				|	EXT_CALL zend_jit_invalid_property_write, REG0
12658				|	SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR, TMP1w, TMP2
12659			} else {
12660				|	EXT_CALL zend_jit_invalid_property_read, REG0
12661				|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
12662			}
12663			|	b >9
12664		} else {
12665			|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
12666			|	b >9
12667		}
12668	}
12669
12670	if (!prop_info
12671	 && may_be_dynamic
12672	 && opline->opcode != ZEND_FETCH_OBJ_W) {
12673		|8:
12674		|	mov FCARG2x, REG0
12675		|	SET_EX_OPLINE opline, REG0
12676		if (opline->opcode != ZEND_FETCH_OBJ_IS) {
12677			|	EXT_CALL zend_jit_fetch_obj_r_dynamic, REG0
12678		} else {
12679			|	EXT_CALL zend_jit_fetch_obj_is_dynamic, REG0
12680		}
12681		|	b >9
12682	}
12683
12684	|.code;
12685	|9: // END
12686	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
12687		if (opline->op1_type == IS_VAR
12688		 && opline->opcode == ZEND_FETCH_OBJ_W
12689		 && (op1_info & MAY_BE_RC1)) {
12690			zend_jit_addr orig_op1_addr = OP1_ADDR();
12691
12692			|	IF_NOT_ZVAL_REFCOUNTED orig_op1_addr, >1, ZREG_TMP1, ZREG_TMP2
12693			|	GET_ZVAL_PTR FCARG1x, orig_op1_addr, TMP1
12694			|	GC_DELREF FCARG1x, TMP1w
12695			|	bne >1
12696			|	SET_EX_OPLINE opline, REG0
12697			|	EXT_CALL zend_jit_extract_helper, REG0
12698			|1:
12699		} else if (!op1_avoid_refcounting) {
12700			if (on_this) {
12701				op1_info &= ~MAY_BE_RC1;
12702			}
12703			|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
12704		}
12705	}
12706
12707	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
12708	 && prop_info
12709	 && (opline->opcode != ZEND_FETCH_OBJ_W ||
12710	     !(opline->extended_value & ZEND_FETCH_OBJ_FLAGS) ||
12711	     !ZEND_TYPE_IS_SET(prop_info->type))
12712	 && (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || on_this || op1_indirect)) {
12713		may_throw = 0;
12714	}
12715
12716	if (may_throw) {
12717		if (!zend_jit_check_exception(Dst)) {
12718			return 0;
12719		}
12720	}
12721
12722	return 1;
12723}
12724
12725static int zend_jit_incdec_obj(dasm_State          **Dst,
12726                               const zend_op        *opline,
12727                               const zend_op_array  *op_array,
12728                               zend_ssa             *ssa,
12729                               const zend_ssa_op    *ssa_op,
12730                               uint32_t              op1_info,
12731                               zend_jit_addr         op1_addr,
12732                               bool                  op1_indirect,
12733                               zend_class_entry     *ce,
12734                               bool                  ce_is_instanceof,
12735                               bool                  on_this,
12736                               bool                  delayed_fetch_this,
12737                               zend_class_entry     *trace_ce,
12738                               uint8_t               prop_type)
12739{
12740	zval *member;
12741	zend_string *name;
12742	zend_property_info *prop_info;
12743	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
12744	zend_jit_addr res_addr = 0;
12745	zend_jit_addr prop_addr;
12746	bool needs_slow_path = 0;
12747	bool use_prop_guard = 0;
12748	bool may_throw = 0;
12749	uint32_t res_info = (opline->result_type != IS_UNDEF) ? RES_INFO() : 0;
12750
12751	ZEND_ASSERT(opline->op2_type == IS_CONST);
12752	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
12753
12754	if (opline->result_type != IS_UNUSED) {
12755		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
12756	}
12757
12758	member = RT_CONSTANT(opline, opline->op2);
12759	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
12760	name = Z_STR_P(member);
12761	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
12762
12763	if (on_this) {
12764		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
12765	} else {
12766		if (opline->op1_type == IS_VAR
12767		 && (op1_info & MAY_BE_INDIRECT)
12768		 && Z_REG(op1_addr) == ZREG_FP) {
12769			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
12770			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
12771			|	GET_Z_PTR FCARG1x, FCARG1x
12772			|1:
12773			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12774		}
12775		if (op1_info & MAY_BE_REF) {
12776			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12777				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
12778			}
12779			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
12780			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12781		}
12782		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
12783			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12784				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12785				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12786
12787				if (!exit_addr) {
12788					return 0;
12789				}
12790				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
12791			} else {
12792				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
12793				|.cold_code
12794				|1:
12795				|	SET_EX_OPLINE opline, REG0
12796				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
12797					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
12798				}
12799				|	LOAD_ADDR FCARG2x, ZSTR_VAL(name)
12800				|	EXT_CALL zend_jit_invalid_property_incdec, REG0
12801				|	b ->exception_handler
12802				|.code
12803			}
12804		}
12805		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
12806	}
12807
12808	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
12809		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
12810		if (prop_info) {
12811			ce = trace_ce;
12812			ce_is_instanceof = 0;
12813			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
12814				if (on_this && JIT_G(current_frame)
12815				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
12816					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
12817				} else if (zend_jit_class_guard(Dst, opline, ce)) {
12818					if (on_this && JIT_G(current_frame)) {
12819						JIT_G(current_frame)->ce = ce;
12820						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
12821					}
12822				} else {
12823					return 0;
12824				}
12825				if (ssa->var_info && ssa_op->op1_use >= 0) {
12826					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
12827					ssa->var_info[ssa_op->op1_use].ce = ce;
12828					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
12829				}
12830				if (ssa->var_info && ssa_op->op1_def >= 0) {
12831					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
12832					ssa->var_info[ssa_op->op1_def].ce = ce;
12833					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
12834				}
12835			}
12836		}
12837	}
12838
12839	use_prop_guard = (prop_type != IS_UNKNOWN
12840		&& prop_type != IS_UNDEF
12841		&& prop_type != IS_REFERENCE
12842		&& (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT);
12843
12844	if (!prop_info) {
12845		needs_slow_path = 1;
12846
12847		|	ldr REG0, EX->run_time_cache
12848		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, opline->extended_value, TMP1
12849		|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
12850		|	cmp REG2, TMP1
12851		|	bne >7
12852		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
12853			|	MEM_ACCESS_64_WITH_UOFFSET ldr, TMP1, REG0, (opline->extended_value + sizeof(void*) * 2), TMP1
12854			|	cbnz TMP1, >7
12855		}
12856		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->extended_value + sizeof(void*)), TMP1
12857		|	tst REG0, REG0
12858		|	blt >7
12859		|	add TMP1, FCARG1x, REG0
12860		if (!use_prop_guard) {
12861			|	ldrb TMP2w, [TMP1, #offsetof(zval,u1.v.type)]
12862			|	IF_TYPE TMP2w , IS_UNDEF, >7
12863		}
12864		|	mov FCARG1x, TMP1
12865		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12866	} else {
12867		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
12868		if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) {
12869			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
12870				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
12871				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12872
12873				if (!exit_addr) {
12874					return 0;
12875				}
12876				|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
12877				|	IF_TYPE TMP2w, IS_UNDEF, &exit_addr
12878			} else {
12879				|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
12880				|	IF_TYPE TMP2w, IS_UNDEF, >7
12881				needs_slow_path = 1;
12882			}
12883		}
12884		if (ZEND_TYPE_IS_SET(prop_info->type)) {
12885			may_throw = 1;
12886			|	SET_EX_OPLINE opline, REG0
12887			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
12888				|	LOAD_ADDR FCARG2x, prop_info
12889			} else {
12890				int prop_info_offset =
12891					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
12892
12893				|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
12894				|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
12895				|	MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
12896			}
12897			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
12898			if (opline->result_type == IS_UNUSED) {
12899				switch (opline->opcode) {
12900					case ZEND_PRE_INC_OBJ:
12901					case ZEND_POST_INC_OBJ:
12902						|	EXT_CALL zend_jit_inc_typed_prop, REG0
12903						break;
12904					case ZEND_PRE_DEC_OBJ:
12905					case ZEND_POST_DEC_OBJ:
12906						|	EXT_CALL zend_jit_dec_typed_prop, REG0
12907						break;
12908					default:
12909						ZEND_UNREACHABLE();
12910				}
12911			} else {
12912				|	LOAD_ZVAL_ADDR CARG3, res_addr
12913				switch (opline->opcode) {
12914					case ZEND_PRE_INC_OBJ:
12915						|	EXT_CALL zend_jit_pre_inc_typed_prop, REG0
12916						break;
12917					case ZEND_PRE_DEC_OBJ:
12918						|	EXT_CALL zend_jit_pre_dec_typed_prop, REG0
12919						break;
12920					case ZEND_POST_INC_OBJ:
12921						|	EXT_CALL zend_jit_post_inc_typed_prop, REG0
12922						break;
12923					case ZEND_POST_DEC_OBJ:
12924						|	EXT_CALL zend_jit_post_dec_typed_prop, REG0
12925						break;
12926					default:
12927						ZEND_UNREACHABLE();
12928				}
12929			}
12930		}
12931	}
12932
12933	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
12934		uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
12935		zend_jit_addr var_addr = prop_addr;
12936
12937		if (use_prop_guard) {
12938			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
12939			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
12940			if (!exit_addr) {
12941				return 0;
12942			}
12943
12944			|	IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr, ZREG_TMP1
12945			var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
12946		}
12947
12948		if (var_info & MAY_BE_REF) {
12949			may_throw = 1;
12950			var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
12951			if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) {
12952				|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
12953			}
12954			|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2, ZREG_TMP1
12955			|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
12956			|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
12957			|	cbnz TMP1, >1
12958			|	add FCARG1x, FCARG1x, #offsetof(zend_reference, val)
12959			|.cold_code
12960			|1:
12961			if (opline) {
12962				|	SET_EX_OPLINE opline, REG0
12963			}
12964			if (opline->result_type == IS_UNUSED) {
12965				|	mov FCARG2x, xzr
12966			} else {
12967				|	LOAD_ZVAL_ADDR FCARG2x, res_addr
12968			}
12969			switch (opline->opcode) {
12970				case ZEND_PRE_INC_OBJ:
12971					|	EXT_CALL zend_jit_pre_inc_typed_ref, REG0
12972					break;
12973				case ZEND_PRE_DEC_OBJ:
12974					|	EXT_CALL zend_jit_pre_dec_typed_ref, REG0
12975					break;
12976				case ZEND_POST_INC_OBJ:
12977					|	EXT_CALL zend_jit_post_inc_typed_ref, REG0
12978					break;
12979				case ZEND_POST_DEC_OBJ:
12980					|	EXT_CALL zend_jit_post_dec_typed_ref, REG0
12981					break;
12982				default:
12983					ZEND_UNREACHABLE();
12984			}
12985			|	b >9
12986			|.code
12987			|2:
12988		}
12989
12990		if (var_info & MAY_BE_LONG) {
12991			if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) {
12992				|	IF_NOT_ZVAL_TYPE var_addr, IS_LONG, >2, ZREG_TMP1
12993			}
12994			if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
12995				if (opline->result_type != IS_UNUSED) {
12996					|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
12997				}
12998			}
12999			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13000				|	LONG_ADD_SUB_WITH_IMM adds, var_addr, Z_L(1), TMP1, TMP2
13001			} else {
13002				|	LONG_ADD_SUB_WITH_IMM subs, var_addr, Z_L(1), TMP1, TMP2
13003			}
13004			|	bvs	>3
13005			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) {
13006				if (opline->result_type != IS_UNUSED) {
13007					|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
13008				}
13009			}
13010			|.cold_code
13011		}
13012		if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) {
13013			if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
13014				may_throw = 1;
13015			}
13016			if (var_info & MAY_BE_LONG) {
13017				|2:
13018			}
13019			if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
13020				var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13021				|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
13022			}
13023			if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) {
13024				|	ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_ANY, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
13025				|	TRY_ADDREF MAY_BE_ANY, REG0w, REG2, TMP1w
13026			}
13027			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13028				if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13029					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
13030					|	EXT_CALL zend_jit_pre_inc, REG0
13031				} else {
13032					|	EXT_CALL increment_function, REG0
13033				}
13034			} else {
13035				if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13036					|	LOAD_ZVAL_ADDR FCARG2x, res_addr
13037					|	EXT_CALL zend_jit_pre_dec, REG0
13038				} else {
13039					|	EXT_CALL decrement_function, REG0
13040				}
13041			}
13042			if (var_info & MAY_BE_LONG) {
13043				|	b >4
13044			}
13045		}
13046
13047		if (var_info & MAY_BE_LONG) {
13048			|3:
13049			if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) {
13050				uint64_t val = 0x43e0000000000000;
13051				|	LOAD_64BIT_VAL TMP2, val
13052				|	SET_ZVAL_LVAL_FROM_REG var_addr, TMP2, TMP1
13053				|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE, TMP1w, TMP2
13054				if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) {
13055					|	SET_ZVAL_LVAL_FROM_REG res_addr, TMP2, TMP1
13056					|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
13057				}
13058			} else {
13059				uint64_t val = 0xc3e0000000000000;
13060				|	LOAD_64BIT_VAL TMP2, val
13061				|	SET_ZVAL_LVAL_FROM_REG var_addr, TMP2, TMP1
13062				|	SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE, TMP1w, TMP2
13063				if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) {
13064					|	SET_ZVAL_LVAL_FROM_REG res_addr, TMP2, TMP1
13065					|	SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2
13066				}
13067			}
13068			if (opline->result_type != IS_UNUSED
13069			 && (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ)
13070			 && prop_info
13071			 && !ZEND_TYPE_IS_SET(prop_info->type)
13072			 && (res_info & MAY_BE_GUARD)
13073			 && (res_info & MAY_BE_LONG)) {
13074				zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
13075				uint32_t old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
13076				int32_t exit_point;
13077				const void *exit_addr;
13078
13079				SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0);
13080				exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
13081				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13082				if (!exit_addr) {
13083					return 0;
13084				}
13085				SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
13086				ssa->var_info[ssa_op->result_def].type = res_info & ~MAY_BE_GUARD;
13087				|	b &exit_addr
13088				|.code
13089			} else {
13090				|	b >4
13091				|.code
13092				|4:
13093			}
13094		}
13095	}
13096
13097	if (needs_slow_path) {
13098		may_throw = 1;
13099		|.cold_code
13100		|7:
13101		|	SET_EX_OPLINE opline, REG0
13102		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
13103		|	LOAD_ADDR FCARG2x, name
13104		|	ldr CARG3, EX->run_time_cache
13105		|	ADD_SUB_64_WITH_CONST_32 add, CARG3, CARG3, opline->extended_value, TMP1
13106		if (opline->result_type == IS_UNUSED) {
13107			|	mov CARG4, xzr
13108		} else {
13109			|	LOAD_ZVAL_ADDR CARG4, res_addr
13110		}
13111
13112		switch (opline->opcode) {
13113			case ZEND_PRE_INC_OBJ:
13114				|	EXT_CALL zend_jit_pre_inc_obj_helper, REG0
13115				break;
13116			case ZEND_PRE_DEC_OBJ:
13117				|	EXT_CALL zend_jit_pre_dec_obj_helper, REG0
13118				break;
13119			case ZEND_POST_INC_OBJ:
13120				|	EXT_CALL zend_jit_post_inc_obj_helper, REG0
13121				break;
13122			case ZEND_POST_DEC_OBJ:
13123				|	EXT_CALL zend_jit_post_dec_obj_helper, REG0
13124				break;
13125			default:
13126				ZEND_UNREACHABLE();
13127		}
13128
13129		|	b >9
13130		|.code
13131	}
13132
13133	|9:
13134	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
13135		if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
13136			may_throw = 1;
13137		}
13138		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
13139	}
13140
13141	if (may_throw) {
13142		if (!zend_jit_check_exception(Dst)) {
13143			return 0;
13144		}
13145	}
13146
13147	return 1;
13148}
13149
13150static int zend_jit_assign_obj_op(dasm_State          **Dst,
13151                                  const zend_op        *opline,
13152                                  const zend_op_array  *op_array,
13153                                  zend_ssa             *ssa,
13154                                  const zend_ssa_op    *ssa_op,
13155                                  uint32_t              op1_info,
13156                                  zend_jit_addr         op1_addr,
13157                                  uint32_t              val_info,
13158                                  zend_ssa_range       *val_range,
13159                                  bool                  op1_indirect,
13160                                  zend_class_entry     *ce,
13161                                  bool                  ce_is_instanceof,
13162                                  bool                  on_this,
13163                                  bool                  delayed_fetch_this,
13164                                  zend_class_entry     *trace_ce,
13165                                  uint8_t               prop_type)
13166{
13167	zval *member;
13168	zend_string *name;
13169	zend_property_info *prop_info;
13170	zend_jit_addr val_addr = OP1_DATA_ADDR();
13171	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
13172	zend_jit_addr prop_addr;
13173	bool needs_slow_path = 0;
13174	bool use_prop_guard = 0;
13175	bool may_throw = 0;
13176	binary_op_type binary_op = get_binary_op(opline->extended_value);
13177
13178	ZEND_ASSERT(opline->op2_type == IS_CONST);
13179	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
13180	ZEND_ASSERT(opline->result_type == IS_UNUSED);
13181
13182	member = RT_CONSTANT(opline, opline->op2);
13183	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
13184	name = Z_STR_P(member);
13185	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
13186
13187	if (on_this) {
13188		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
13189	} else {
13190		if (opline->op1_type == IS_VAR
13191		 && (op1_info & MAY_BE_INDIRECT)
13192		 && Z_REG(op1_addr) == ZREG_FP) {
13193			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
13194			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
13195			|	GET_Z_PTR FCARG1x, FCARG1x
13196			|1:
13197			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13198		}
13199		if (op1_info & MAY_BE_REF) {
13200			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13201				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
13202			}
13203			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
13204			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13205		}
13206		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
13207			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13208				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13209				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13210
13211				if (!exit_addr) {
13212					return 0;
13213				}
13214				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
13215			} else {
13216				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
13217				|.cold_code
13218				|1:
13219				|	SET_EX_OPLINE opline, REG0
13220				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13221					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
13222				}
13223				|	LOAD_ADDR FCARG2x, ZSTR_VAL(name)
13224				if (op1_info & MAY_BE_UNDEF) {
13225					|	EXT_CALL zend_jit_invalid_property_assign_op, REG0
13226				} else {
13227					|	EXT_CALL zend_jit_invalid_property_assign, REG0
13228				}
13229				may_throw = 1;
13230				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
13231				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13232					|	b >8
13233				} else {
13234					|	b >9
13235				}
13236				|.code
13237			}
13238		}
13239		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
13240	}
13241
13242	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
13243		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
13244		if (prop_info) {
13245			ce = trace_ce;
13246			ce_is_instanceof = 0;
13247			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
13248				if (on_this && JIT_G(current_frame)
13249				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
13250					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
13251				} else if (zend_jit_class_guard(Dst, opline, ce)) {
13252					if (on_this && JIT_G(current_frame)) {
13253						JIT_G(current_frame)->ce = ce;
13254						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
13255					}
13256				} else {
13257					return 0;
13258				}
13259				if (ssa->var_info && ssa_op->op1_use >= 0) {
13260					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
13261					ssa->var_info[ssa_op->op1_use].ce = ce;
13262					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
13263				}
13264				if (ssa->var_info && ssa_op->op1_def >= 0) {
13265					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
13266					ssa->var_info[ssa_op->op1_def].ce = ce;
13267					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
13268				}
13269			}
13270		}
13271	}
13272
13273	use_prop_guard = (prop_type != IS_UNKNOWN
13274		&& prop_type != IS_UNDEF
13275		&& prop_type != IS_REFERENCE
13276		&& (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT);
13277
13278	if (!prop_info) {
13279		needs_slow_path = 1;
13280
13281		|	ldr REG0, EX->run_time_cache
13282		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, ((opline+1)->extended_value), TMP1
13283		|	ldr TMP2, [FCARG1x, #offsetof(zend_object, ce)]
13284		|	cmp REG2, TMP2
13285		|	bne >7
13286		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
13287			|	MEM_ACCESS_64_WITH_UOFFSET ldr, TMP1, REG0, ((opline+1)->extended_value + sizeof(void*) * 2), TMP1
13288			|	cbnz TMP1, >7
13289		}
13290		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, ((opline+1)->extended_value + sizeof(void*)), TMP1
13291		|	tst REG0, REG0
13292		|	blt >7
13293		|	add TMP1, FCARG1x, REG0
13294		if (!use_prop_guard) {
13295			|	ldrb TMP2w, [TMP1, #offsetof(zval,u1.v.type)]
13296			|	IF_TYPE TMP2w, IS_UNDEF, >7
13297		}
13298		|	mov FCARG1x, TMP1
13299		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13300	} else {
13301		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
13302		if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) {
13303			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13304				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13305				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13306
13307				if (!exit_addr) {
13308					return 0;
13309				}
13310				|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
13311				|	IF_TYPE TMP2w, IS_UNDEF, &exit_addr
13312			} else {
13313				|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
13314				|	IF_TYPE TMP2w, IS_UNDEF, >7
13315				needs_slow_path = 1;
13316			}
13317		}
13318		if (ZEND_TYPE_IS_SET(prop_info->type)) {
13319			uint32_t info = val_info;
13320
13321			may_throw = 1;
13322
13323			if (opline) {
13324				|	SET_EX_OPLINE opline, REG0
13325			}
13326
13327			|	IF_ZVAL_TYPE prop_addr, IS_REFERENCE, >1, ZREG_TMP1
13328			|.cold_code
13329			|1:
13330			|	GET_ZVAL_PTR FCARG1x, prop_addr, TMP1
13331			if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
13332				|	LOAD_ZVAL_ADDR FCARG2x, val_addr
13333			}
13334			|	LOAD_ADDR CARG3, binary_op
13335			if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
13336			 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13337				|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0
13338			} else {
13339				|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
13340			}
13341			|	b >9
13342			|.code
13343
13344			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
13345
13346			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
13347				|	LOAD_ADDR FCARG2x, prop_info
13348			} else {
13349				int prop_info_offset =
13350					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
13351
13352				|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
13353				|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
13354				|	MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
13355			}
13356			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
13357			|	LOAD_ZVAL_ADDR CARG3, val_addr
13358			|	LOAD_ADDR CARG4, binary_op
13359
13360			|	EXT_CALL zend_jit_assign_op_to_typed_prop, REG0
13361
13362			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
13363				info |= MAY_BE_RC1|MAY_BE_RCN;
13364			}
13365
13366			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL, ZREG_TMP1, ZREG_TMP2
13367		}
13368	}
13369
13370	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
13371		zend_jit_addr var_addr = prop_addr;
13372		uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
13373		uint32_t var_def_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
13374
13375		if (use_prop_guard) {
13376			int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
13377			const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13378			if (!exit_addr) {
13379				return 0;
13380			}
13381
13382			|	IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr, ZREG_TMP1
13383			var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF));
13384		}
13385
13386		if (var_info & MAY_BE_REF) {
13387			may_throw = 1;
13388			var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
13389			|	LOAD_ZVAL_ADDR REG0, prop_addr
13390			|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2, ZREG_TMP1
13391			|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
13392			|	ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)]
13393			|	cbnz TMP1, >1
13394			|	add REG0, FCARG1x, #offsetof(zend_reference, val)
13395			|.cold_code
13396			|1:
13397			if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) {
13398				|	LOAD_ZVAL_ADDR FCARG2x, val_addr
13399			}
13400			if (opline) {
13401				|	SET_EX_OPLINE opline, REG0
13402			}
13403			|	LOAD_ADDR CARG3, binary_op
13404			if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR))
13405			 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13406				|	EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0
13407			} else {
13408				|	EXT_CALL zend_jit_assign_op_to_typed_ref, REG0
13409			}
13410			|	b >9
13411			|.code
13412			|2:
13413		}
13414
13415		switch (opline->extended_value) {
13416			case ZEND_ADD:
13417			case ZEND_SUB:
13418			case ZEND_MUL:
13419				if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
13420			    (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13421					if (opline->extended_value != ZEND_ADD ||
13422					    (var_info & MAY_BE_ANY) != MAY_BE_ARRAY ||
13423					    (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) {
13424						may_throw = 1;
13425					}
13426				}
13427				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,
13428						1 /* may overflow */, 0)) {
13429					return 0;
13430				}
13431				break;
13432			case ZEND_BW_OR:
13433			case ZEND_BW_AND:
13434			case ZEND_BW_XOR:
13435				may_throw = 1;
13436				if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
13437				    (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13438					if ((var_info & MAY_BE_ANY) != MAY_BE_STRING ||
13439					    (val_info & MAY_BE_ANY) != MAY_BE_STRING) {
13440						may_throw = 1;
13441					}
13442				}
13443				goto long_math;
13444			case ZEND_SL:
13445			case ZEND_SR:
13446				if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
13447				    (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13448					may_throw = 1;
13449				}
13450				if ((opline+1)->op1_type != IS_CONST ||
13451				    Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG ||
13452				    Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) < 0) {
13453					may_throw = 1;
13454				}
13455				goto long_math;
13456			case ZEND_MOD:
13457				if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
13458				    (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13459					if (opline->extended_value != ZEND_ADD ||
13460					    (var_info & MAY_BE_ANY) != MAY_BE_ARRAY ||
13461					    (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) {
13462						may_throw = 1;
13463					}
13464				}
13465				if ((opline+1)->op1_type != IS_CONST ||
13466				    Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG ||
13467				    Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) == 0) {
13468					may_throw = 1;
13469				}
13470long_math:
13471				if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value,
13472						IS_CV, opline->op1, var_addr, var_info, NULL,
13473						(opline+1)->op1_type, (opline+1)->op1, val_addr, val_info,
13474						val_range,
13475						0, var_addr, var_def_info, var_info, 0)) {
13476					return 0;
13477				}
13478				break;
13479			case ZEND_CONCAT:
13480				may_throw = 1;
13481				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,
13482						0)) {
13483					return 0;
13484				}
13485				break;
13486			default:
13487				ZEND_UNREACHABLE();
13488		}
13489	}
13490
13491	if (needs_slow_path) {
13492		may_throw = 1;
13493		|.cold_code
13494		|7:
13495		|	SET_EX_OPLINE opline, REG0
13496		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
13497		|	LOAD_ADDR FCARG2x, name
13498		|	LOAD_ZVAL_ADDR CARG3, val_addr
13499		|	ldr CARG4, EX->run_time_cache
13500		|	ADD_SUB_64_WITH_CONST_32 add, CARG4, CARG4, (opline+1)->extended_value, TMP1
13501		|	LOAD_ADDR CARG5, binary_op
13502		|	EXT_CALL zend_jit_assign_obj_op_helper, REG0
13503
13504		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
13505			val_info |= MAY_BE_RC1|MAY_BE_RCN;
13506		}
13507
13508		|8:
13509		|	// FREE_OP_DATA();
13510		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2
13511		|	b >9
13512		|.code
13513	}
13514
13515	|9:
13516	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
13517		if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) {
13518			may_throw = 1;
13519		}
13520		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
13521	}
13522
13523	if (may_throw) {
13524		if (!zend_jit_check_exception(Dst)) {
13525			return 0;
13526		}
13527	}
13528
13529	return 1;
13530}
13531
13532static int zend_jit_assign_obj(dasm_State          **Dst,
13533                               const zend_op        *opline,
13534                               const zend_op_array  *op_array,
13535                               zend_ssa             *ssa,
13536                               const zend_ssa_op    *ssa_op,
13537                               uint32_t              op1_info,
13538                               zend_jit_addr         op1_addr,
13539                               uint32_t              val_info,
13540                               bool                  op1_indirect,
13541                               zend_class_entry     *ce,
13542                               bool                  ce_is_instanceof,
13543                               bool                  on_this,
13544                               bool                  delayed_fetch_this,
13545                               zend_class_entry     *trace_ce,
13546                               uint8_t               prop_type,
13547                               int                   may_throw)
13548{
13549	zval *member;
13550	zend_string *name;
13551	zend_property_info *prop_info;
13552	zend_jit_addr val_addr = OP1_DATA_ADDR();
13553	zend_jit_addr res_addr = 0;
13554	zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
13555	zend_jit_addr prop_addr;
13556	bool needs_slow_path = 0;
13557	bool needs_val_dtor = 0;
13558
13559	if (RETURN_VALUE_USED(opline)) {
13560		res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
13561	}
13562
13563	ZEND_ASSERT(opline->op2_type == IS_CONST);
13564	ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
13565
13566	member = RT_CONSTANT(opline, opline->op2);
13567	ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0');
13568	name = Z_STR_P(member);
13569	prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename);
13570
13571	if (on_this) {
13572		|	GET_ZVAL_PTR FCARG1x, this_addr, TMP1
13573	} else {
13574		if (opline->op1_type == IS_VAR
13575		 && (op1_info & MAY_BE_INDIRECT)
13576		 && Z_REG(op1_addr) == ZREG_FP) {
13577			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
13578			|	IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w
13579			|	GET_Z_PTR FCARG1x, FCARG1x
13580			|1:
13581			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13582		}
13583		if (op1_info & MAY_BE_REF) {
13584			if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13585				|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
13586			}
13587			|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
13588			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13589		}
13590		if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) {
13591			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13592				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13593				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13594
13595				if (!exit_addr) {
13596					return 0;
13597				}
13598				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1
13599			} else {
13600				|	IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1
13601				|.cold_code
13602				|1:
13603				|	SET_EX_OPLINE opline, REG0
13604				if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
13605					|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
13606				}
13607				|	LOAD_ADDR FCARG2x, ZSTR_VAL(name)
13608				|	EXT_CALL zend_jit_invalid_property_assign, REG0
13609				if (RETURN_VALUE_USED(opline)) {
13610					|	SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2
13611				}
13612				if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
13613				 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13614					needs_val_dtor = 1;
13615					|	b >7
13616				} else {
13617					|	b >9
13618				}
13619				|.code
13620			}
13621		}
13622		|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
13623	}
13624
13625	if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
13626		prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename);
13627		if (prop_info) {
13628			ce = trace_ce;
13629			ce_is_instanceof = 0;
13630			if (!(op1_info & MAY_BE_CLASS_GUARD)) {
13631				if (on_this && JIT_G(current_frame)
13632				 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) {
13633					ZEND_ASSERT(JIT_G(current_frame)->ce == ce);
13634				} else if (zend_jit_class_guard(Dst, opline, ce)) {
13635					if (on_this && JIT_G(current_frame)) {
13636						JIT_G(current_frame)->ce = ce;
13637						TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame));
13638					}
13639				} else {
13640					return 0;
13641				}
13642				if (ssa->var_info && ssa_op->op1_use >= 0) {
13643					ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD;
13644					ssa->var_info[ssa_op->op1_use].ce = ce;
13645					ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof;
13646				}
13647				if (ssa->var_info && ssa_op->op1_def >= 0) {
13648					ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD;
13649					ssa->var_info[ssa_op->op1_def].ce = ce;
13650					ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof;
13651				}
13652			}
13653		}
13654	}
13655
13656	if (!prop_info) {
13657		needs_slow_path = 1;
13658
13659		|	ldr REG0, EX->run_time_cache
13660		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, opline->extended_value, TMP1
13661		|	ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)]
13662		|	cmp REG2, TMP1
13663		|	bne >5
13664		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
13665			|	MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, (opline->extended_value + sizeof(void*) * 2), TMP1
13666		}
13667		|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->extended_value + sizeof(void*)), TMP1
13668		|	tst REG0, REG0
13669		|	blt >5
13670		|	add TMP2, FCARG1x, REG0
13671		|	ldrb TMP1w, [TMP2, #offsetof(zval,u1.v.type)]
13672		|	IF_TYPE TMP1w, IS_UNDEF, >5
13673		|	mov FCARG1x, TMP2
13674		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
13675		if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
13676			|	cbnz FCARG2x, >1
13677			|.cold_code
13678			|1:
13679			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
13680			|	SET_EX_OPLINE opline, REG0
13681			|	LOAD_ZVAL_ADDR CARG3, val_addr
13682			if (RETURN_VALUE_USED(opline)) {
13683				|	LOAD_ZVAL_ADDR CARG4, res_addr
13684			} else {
13685				|	mov CARG4, xzr
13686			}
13687
13688			|	EXT_CALL zend_jit_assign_to_typed_prop, REG0
13689
13690			if ((opline+1)->op1_type == IS_CONST) {
13691				|	// TODO: ???
13692				|	// if (Z_TYPE_P(value) == orig_type) {
13693				|	// CACHE_PTR_EX(cache_slot + 2, NULL);
13694			}
13695
13696			if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))
13697			 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
13698				|	b >7
13699			} else {
13700				|	b >9
13701			}
13702			|.code
13703		}
13704	} else {
13705		prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset);
13706		if (!ce || ce_is_instanceof || !(ce->ce_flags & ZEND_ACC_IMMUTABLE) || ce->__get || ce->__set || (prop_info->flags & ZEND_ACC_READONLY)) {
13707			// Undefined property with magic __get()/__set()
13708			if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13709				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13710				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13711
13712				if (!exit_addr) {
13713					return 0;
13714				}
13715				|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
13716				|	IF_TYPE TMP2w, IS_UNDEF, &exit_addr
13717			} else {
13718				|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1
13719				|	IF_TYPE TMP2w, IS_UNDEF, >5
13720				needs_slow_path = 1;
13721			}
13722		}
13723		if (ZEND_TYPE_IS_SET(prop_info->type)) {
13724			uint32_t info = val_info;
13725
13726			|	// value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
13727			|	SET_EX_OPLINE opline, REG0
13728			if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) {
13729				|	LOAD_ADDR FCARG2x, prop_info
13730			} else {
13731				int prop_info_offset =
13732					(((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*));
13733
13734				|	ldr REG0, [FCARG1x, #offsetof(zend_object, ce)]
13735				|	ldr	REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)]
13736				|	MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1
13737			}
13738			|	LOAD_ZVAL_ADDR FCARG1x, prop_addr
13739			|	LOAD_ZVAL_ADDR CARG3, val_addr
13740			if (RETURN_VALUE_USED(opline)) {
13741				|	LOAD_ZVAL_ADDR CARG4, res_addr
13742			} else {
13743				|	mov CARG4, xzr
13744			}
13745
13746			|	EXT_CALL zend_jit_assign_to_typed_prop, REG0
13747
13748			if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
13749				info |= MAY_BE_RC1|MAY_BE_RCN;
13750			}
13751
13752			|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL, ZREG_TMP1, ZREG_TMP2
13753		}
13754	}
13755
13756	if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
13757		// value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES());
13758		if (opline->result_type == IS_UNUSED) {
13759			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)) {
13760				return 0;
13761			}
13762		} else {
13763			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)) {
13764				return 0;
13765			}
13766		}
13767	}
13768
13769	if (needs_slow_path) {
13770		|.cold_code
13771		|5:
13772		|	SET_EX_OPLINE opline, REG0
13773		|	// value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
13774		|	LOAD_ADDR FCARG2x, name
13775
13776		|	LOAD_ZVAL_ADDR CARG3, val_addr
13777		|	ldr CARG4, EX->run_time_cache
13778		|	ADD_SUB_64_WITH_CONST_32 add, CARG4, CARG4, opline->extended_value, TMP1
13779		if (RETURN_VALUE_USED(opline)) {
13780			|	LOAD_ZVAL_ADDR CARG5, res_addr
13781		} else {
13782			|	mov CARG5, xzr
13783		}
13784
13785		|	EXT_CALL zend_jit_assign_obj_helper, REG0
13786
13787		if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
13788			val_info |= MAY_BE_RC1|MAY_BE_RCN;
13789		}
13790
13791		|7:
13792		|	// FREE_OP_DATA();
13793		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2
13794		|	b >9
13795		|.code
13796	} else if (needs_val_dtor) {
13797		|.cold_code
13798		|7:
13799		|	// FREE_OP_DATA();
13800		|	FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2
13801		|	b >9
13802		|.code
13803	}
13804
13805	|9:
13806	if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
13807		|	FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
13808	}
13809
13810	if (may_throw) {
13811		if (!zend_jit_check_exception(Dst)) {
13812			return 0;
13813		}
13814	}
13815
13816	return 1;
13817}
13818
13819static int zend_jit_free(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, int may_throw)
13820{
13821	zend_jit_addr op1_addr = OP1_ADDR();
13822
13823	if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
13824		if (may_throw) {
13825			|	SET_EX_OPLINE opline, REG0
13826		}
13827		if (opline->opcode == ZEND_FE_FREE && (op1_info & (MAY_BE_OBJECT|MAY_BE_REF))) {
13828			if (op1_info & MAY_BE_ARRAY) {
13829				|	IF_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1
13830			}
13831			|	MEM_ACCESS_32_WITH_UOFFSET ldr, FCARG1w, FP, (opline->op1.var + offsetof(zval, u2.fe_iter_idx)), TMP1
13832			|	mvn TMP1w, wzr // TODO: DynAsm fails loading #-1
13833			|	cmp FCARG1w, TMP1w
13834			|	beq >7
13835			|	EXT_CALL zend_hash_iterator_del, REG0
13836			|7:
13837		}
13838		|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline, ZREG_TMP1, ZREG_TMP2
13839		if (may_throw) {
13840			if (!zend_jit_check_exception(Dst)) {
13841				return 0;
13842			}
13843		}
13844	}
13845	return 1;
13846}
13847
13848static int zend_jit_echo(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
13849{
13850	if (opline->op1_type == IS_CONST) {
13851		zval *zv;
13852		size_t len;
13853
13854		zv = RT_CONSTANT(opline, opline->op1);
13855		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
13856		len = Z_STRLEN_P(zv);
13857
13858		if (len > 0) {
13859			const char *str = Z_STRVAL_P(zv);
13860
13861			|	SET_EX_OPLINE opline, REG0
13862			|	LOAD_ADDR CARG1, str
13863			|	LOAD_64BIT_VAL CARG2, len
13864			|	EXT_CALL zend_write, REG0
13865			if (!zend_jit_check_exception(Dst)) {
13866				return 0;
13867			}
13868		}
13869	} else {
13870		zend_jit_addr op1_addr = OP1_ADDR();
13871
13872		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
13873
13874		|	SET_EX_OPLINE opline, REG0
13875		|	GET_ZVAL_PTR REG0, op1_addr, TMP1
13876		|	add CARG1, REG0, #offsetof(zend_string, val)
13877		|	ldr CARG2, [REG0, #offsetof(zend_string, len)]
13878		|	EXT_CALL zend_write, REG0
13879		if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
13880			|	ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline, ZREG_TMP1, ZREG_TMP2
13881		}
13882		if (!zend_jit_check_exception(Dst)) {
13883			return 0;
13884		}
13885	}
13886	return 1;
13887}
13888
13889static 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)
13890{
13891	if (opline->op1_type == IS_CONST) {
13892		zval *zv;
13893		size_t len;
13894
13895		zv = RT_CONSTANT(opline, opline->op1);
13896		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
13897		len = Z_STRLEN_P(zv);
13898
13899		|	SET_ZVAL_LVAL res_addr, len, TMP1, TMP2
13900		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
13901			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
13902		} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
13903			return 0;
13904		}
13905	} else {
13906		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
13907
13908		if (Z_MODE(res_addr) == IS_REG) {
13909			|	GET_ZVAL_PTR Rx(Z_REG(res_addr)), op1_addr, TMP1
13910			|	ldr Rx(Z_REG(res_addr)), [Rx(Z_REG(res_addr)), #offsetof(zend_string, len)]
13911			if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
13912				return 0;
13913			}
13914		} else {
13915			|	GET_ZVAL_PTR REG0, op1_addr, TMP1
13916			|	ldr REG0, [REG0, #offsetof(zend_string, len)]
13917			|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
13918			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
13919		}
13920		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
13921	}
13922	return 1;
13923}
13924
13925static 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)
13926{
13927	if (opline->op1_type == IS_CONST) {
13928		zval *zv;
13929		zend_long count;
13930
13931		zv = RT_CONSTANT(opline, opline->op1);
13932		ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY);
13933		count = zend_hash_num_elements(Z_ARRVAL_P(zv));
13934
13935		|	SET_ZVAL_LVAL res_addr, count, TMP1, TMP2
13936		if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
13937			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
13938		} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
13939			return 0;
13940		}
13941	} else {
13942		ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY);
13943		// Note: See the implementation of ZEND_COUNT in Zend/zend_vm_def.h - arrays do not contain IS_UNDEF starting in php 8.1+.
13944
13945		if (Z_MODE(res_addr) == IS_REG) {
13946			|	GET_ZVAL_PTR Rx(Z_REG(res_addr)), op1_addr, TMP1
13947			// Sign-extend the 32-bit value to a potentially 64-bit zend_long
13948			|	ldr Rw(Z_REG(res_addr)), [Rx(Z_REG(res_addr)), #offsetof(HashTable, nNumOfElements)]
13949			if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) {
13950				return 0;
13951			}
13952		} else {
13953			|	GET_ZVAL_PTR REG0, op1_addr, TMP1
13954			// Sign-extend the 32-bit value to a potentially 64-bit zend_long
13955			|	ldr REG0w, [REG0, #offsetof(HashTable, nNumOfElements)]
13956			|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
13957			|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
13958		}
13959		|	FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2
13960	}
13961
13962	if (may_throw) {
13963		return zend_jit_check_exception(Dst);
13964	}
13965	return 1;
13966}
13967
13968static int zend_jit_load_this(dasm_State **Dst, uint32_t var)
13969{
13970	zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
13971
13972	|	ldr FCARG1x, EX->This.value.ptr
13973	|	SET_ZVAL_PTR var_addr, FCARG1x, TMP1
13974	|	SET_ZVAL_TYPE_INFO var_addr, IS_OBJECT_EX, TMP1w, TMP2
13975	|	GC_ADDREF FCARG1x, TMP1w
13976	return 1;
13977}
13978
13979static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool check_only)
13980{
13981	if (!op_array->scope || (op_array->fn_flags & ZEND_ACC_STATIC)) {
13982		if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
13983			if (!JIT_G(current_frame) ||
13984			    !TRACE_FRAME_IS_THIS_CHECKED(JIT_G(current_frame))) {
13985
13986				int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
13987				const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
13988
13989				if (!exit_addr) {
13990					return 0;
13991				}
13992
13993				|	ldrb TMP1w, EX->This.u1.v.type
13994				|	cmp TMP1w, #IS_OBJECT
13995				|	bne &exit_addr
13996
13997				if (JIT_G(current_frame)) {
13998					TRACE_FRAME_SET_THIS_CHECKED(JIT_G(current_frame));
13999				}
14000			}
14001		} else {
14002
14003			|	ldrb TMP1w, EX->This.u1.v.type
14004			|	cmp TMP1w, #IS_OBJECT
14005			|	bne >1
14006			|.cold_code
14007			|1:
14008			|	SET_EX_OPLINE opline, REG0
14009			|	b ->invalid_this
14010			|.code
14011		}
14012	}
14013
14014	if (!check_only) {
14015		if (!zend_jit_load_this(Dst, opline->result.var)) {
14016			return 0;
14017		}
14018	}
14019	return 1;
14020}
14021
14022static 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)
14023{
14024	uint32_t count;
14025	Bucket *p;
14026	const zend_op *target;
14027	int b;
14028	int32_t exit_point;
14029	const void *exit_addr;
14030
14031	if (default_label) {
14032		|	cbz REG0, &default_label
14033	} else if (next_opline) {
14034		|	cbz REG0, >3
14035	} else {
14036		|	cbz REG0, =>default_b
14037	}
14038	|	LOAD_ADDR FCARG1x, jumptable
14039	|	ldr TMP1, [FCARG1x, #offsetof(HashTable, arData)]
14040	|	sub REG0, REG0, TMP1
14041	|	mov FCARG1x, #(sizeof(Bucket) / sizeof(void*))
14042	|	sdiv REG0, REG0, FCARG1x
14043	|	adr FCARG1x, >4
14044	|	ldr TMP1, [FCARG1x, REG0]
14045	|	br TMP1
14046
14047	|.jmp_table
14048	|.align 8
14049	|4:
14050	if (trace_info) {
14051		trace_info->jmp_table_size += zend_hash_num_elements(jumptable);
14052	}
14053
14054	count = jumptable->nNumUsed;
14055	p = jumptable->arData;
14056	do {
14057		if (Z_TYPE(p->val) == IS_UNDEF) {
14058			if (default_label) {
14059				|	.addr &default_label
14060			} else if (next_opline) {
14061				|	.addr >3
14062			} else {
14063				|	.addr =>default_b
14064			}
14065		} else {
14066			target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val));
14067			if (!next_opline) {
14068				b = ssa->cfg.map[target - op_array->opcodes];
14069				|	.addr =>b
14070			} else if (next_opline == target) {
14071				|	.addr >3
14072			} else {
14073				exit_point = zend_jit_trace_get_exit_point(target, 0);
14074				exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14075				if (!exit_addr) {
14076					return 0;
14077				}
14078				|	.addr &exit_addr
14079			}
14080		}
14081		p++;
14082		count--;
14083	} while (count);
14084	|.code
14085
14086	return 1;
14087}
14088
14089static 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)
14090{
14091	HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
14092	const zend_op *next_opline = NULL;
14093
14094	if (trace) {
14095		ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
14096		ZEND_ASSERT(trace->opline != NULL);
14097		next_opline = trace->opline;
14098	}
14099
14100	if (opline->op1_type == IS_CONST) {
14101		zval *zv = RT_CONSTANT(opline, opline->op1);
14102		zval *jump_zv = NULL;
14103		int b;
14104
14105		if (opline->opcode == ZEND_SWITCH_LONG) {
14106			if (Z_TYPE_P(zv) == IS_LONG) {
14107				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
14108			}
14109		} else if (opline->opcode == ZEND_SWITCH_STRING) {
14110			if (Z_TYPE_P(zv) == IS_STRING) {
14111				jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv));
14112			}
14113		} else if (opline->opcode == ZEND_MATCH) {
14114			if (Z_TYPE_P(zv) == IS_LONG) {
14115				jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
14116			} else if (Z_TYPE_P(zv) == IS_STRING) {
14117				jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv));
14118			}
14119		} else {
14120			ZEND_UNREACHABLE();
14121		}
14122		if (next_opline) {
14123			const zend_op *target;
14124
14125			if (jump_zv != NULL) {
14126				target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
14127			} else {
14128				target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
14129			}
14130			ZEND_ASSERT(target == next_opline);
14131		} else {
14132			if (jump_zv != NULL) {
14133				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes];
14134			} else {
14135				b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
14136			}
14137			|	b =>b
14138		}
14139	} else {
14140		zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
14141		uint32_t op1_info = OP1_INFO();
14142		zend_jit_addr op1_addr = OP1_ADDR();
14143		const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
14144		const zend_op *target;
14145		int default_b = next_opline ? -1 : ssa->cfg.map[default_opline - op_array->opcodes];
14146		int b;
14147		int32_t exit_point;
14148		const void *fallback_label = NULL;
14149		const void *default_label = NULL;
14150		const void *exit_addr;
14151
14152		if (next_opline) {
14153			if (next_opline != opline + 1) {
14154				exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
14155				fallback_label = zend_jit_trace_get_exit_addr(exit_point);
14156				if (!fallback_label) {
14157					return 0;
14158				}
14159			}
14160			if (next_opline != default_opline) {
14161				exit_point = zend_jit_trace_get_exit_point(default_opline, 0);
14162				default_label = zend_jit_trace_get_exit_addr(exit_point);
14163				if (!default_label) {
14164					return 0;
14165				}
14166			}
14167		}
14168
14169		if (opline->opcode == ZEND_SWITCH_LONG) {
14170			if (op1_info & MAY_BE_LONG) {
14171				if (op1_info & MAY_BE_REF) {
14172					|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >1, ZREG_TMP1
14173					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr, TMP1
14174					|.cold_code
14175					|1:
14176					|	// ZVAL_DEREF(op)
14177					if (fallback_label) {
14178						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label, ZREG_TMP1
14179					} else {
14180						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3, ZREG_TMP1
14181					}
14182					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
14183					if (fallback_label) {
14184						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
14185						|	IF_NOT_Z_TYPE TMP1, IS_LONG, &fallback_label, TMP2w
14186					} else {
14187						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
14188						|	IF_NOT_Z_TYPE TMP1, IS_LONG, >3, TMP2w
14189					}
14190					|	ldr FCARG2x, [FCARG2x, #offsetof(zend_reference, val.value.lval)]
14191					|	b >2
14192					|.code
14193					|2:
14194				} else {
14195					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
14196						if (fallback_label) {
14197							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &fallback_label, ZREG_TMP1
14198						} else {
14199							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1
14200						}
14201					}
14202					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr, TMP1
14203				}
14204				if (HT_IS_PACKED(jumptable)) {
14205					uint32_t count = jumptable->nNumUsed;
14206					Bucket *p = jumptable->arData;
14207
14208					|	CMP_64_WITH_CONST_32 FCARG2x, jumptable->nNumUsed, TMP1
14209					if (default_label) {
14210						|	bhs &default_label
14211					} else if (next_opline) {
14212						|	bhs >3
14213					} else {
14214						|	bhs =>default_b
14215					}
14216					|	adr REG0, >4
14217					|	ldr TMP1, [REG0, FCARG2x, lsl #3]
14218					|	br TMP1
14219
14220					|.jmp_table
14221					|.align 8
14222					|4:
14223					if (trace_info) {
14224						trace_info->jmp_table_size += count;
14225					}
14226					p = jumptable->arData;
14227					do {
14228						if (Z_TYPE(p->val) == IS_UNDEF) {
14229							if (default_label) {
14230								|	.addr &default_label
14231							} else if (next_opline) {
14232								|	.addr >3
14233							} else {
14234								|	.addr =>default_b
14235							}
14236						} else {
14237							target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val));
14238							if (!next_opline) {
14239								b = ssa->cfg.map[target - op_array->opcodes];
14240								|	.addr =>b
14241							} else if (next_opline == target) {
14242								|	.addr >3
14243							} else {
14244								exit_point = zend_jit_trace_get_exit_point(target, 0);
14245								exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14246								if (!exit_addr) {
14247									return 0;
14248								}
14249								|	.addr &exit_addr
14250							}
14251						}
14252						p++;
14253						count--;
14254					} while (count);
14255					|.code
14256					|3:
14257				} else {
14258					|	LOAD_ADDR FCARG1x, jumptable
14259					|	EXT_CALL zend_hash_index_find, REG0
14260					|	mov REG0, RETVALx
14261					if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
14262						return 0;
14263					}
14264					|3:
14265				}
14266			}
14267		} else if (opline->opcode == ZEND_SWITCH_STRING) {
14268			if (op1_info & MAY_BE_STRING) {
14269				if (op1_info & MAY_BE_REF) {
14270					|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >1, ZREG_TMP1
14271					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
14272					|.cold_code
14273					|1:
14274					|	// ZVAL_DEREF(op)
14275					if (fallback_label) {
14276						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label, ZREG_TMP1
14277					} else {
14278						|	IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3, ZREG_TMP1
14279					}
14280					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
14281					if (fallback_label) {
14282						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
14283						|	IF_NOT_Z_TYPE TMP1, IS_STRING, &fallback_label, TMP2w
14284					} else {
14285						|	add TMP1, FCARG2x, #offsetof(zend_reference, val)
14286						|	IF_NOT_Z_TYPE TMP1, IS_STRING, >3, TMP2w
14287					}
14288					|	ldr FCARG2x, [FCARG2x, #offsetof(zend_reference, val.value.ptr)]
14289					|	b >2
14290					|.code
14291					|2:
14292				} else {
14293					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_STRING)) {
14294						if (fallback_label) {
14295							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &fallback_label, ZREG_TMP1
14296						} else {
14297							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3, ZREG_TMP1
14298						}
14299					}
14300					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
14301				}
14302				|	LOAD_ADDR FCARG1x, jumptable
14303				|	EXT_CALL zend_hash_find, REG0
14304				|	mov REG0, RETVALx
14305				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
14306					return 0;
14307				}
14308				|3:
14309			}
14310		} else if (opline->opcode == ZEND_MATCH) {
14311			if (op1_info & (MAY_BE_LONG|MAY_BE_STRING)) {
14312				if (op1_info & MAY_BE_REF) {
14313					|	LOAD_ZVAL_ADDR FCARG2x, op1_addr
14314					|	ZVAL_DEREF FCARG2x, op1_info, TMP1w
14315					op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
14316				}
14317				|	LOAD_ADDR FCARG1x, jumptable
14318				if (op1_info & MAY_BE_LONG) {
14319					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
14320						if (op1_info & MAY_BE_STRING) {
14321							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >5, ZREG_TMP1
14322						} else if (op1_info & MAY_BE_UNDEF) {
14323							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1
14324						} else if (default_label) {
14325							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &default_label, ZREG_TMP1
14326						} else if (next_opline) {
14327							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1
14328						} else {
14329							|	IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, =>default_b, ZREG_TMP1
14330						}
14331					}
14332					|	GET_ZVAL_LVAL ZREG_FCARG2, op1_addr, TMP1
14333					|	EXT_CALL zend_hash_index_find, REG0
14334					|	mov REG0, RETVALx
14335					if (op1_info & MAY_BE_STRING) {
14336						|	b >2
14337					}
14338				}
14339				if (op1_info & MAY_BE_STRING) {
14340					|5:
14341					if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_STRING))) {
14342						if (op1_info & MAY_BE_UNDEF) {
14343							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1
14344						} else if (default_label) {
14345							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &default_label, ZREG_TMP1
14346						} else if (next_opline) {
14347							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3, ZREG_TMP1
14348						} else {
14349							|	IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, =>default_b, ZREG_TMP1
14350						}
14351					}
14352					|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
14353					|	EXT_CALL zend_hash_find, REG0
14354					|	mov REG0, RETVALx
14355				}
14356				|2:
14357				if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) {
14358					return 0;
14359				}
14360			}
14361			if (op1_info & MAY_BE_UNDEF) {
14362				|6:
14363				if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_STRING))) {
14364					if (default_label) {
14365						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, &default_label, ZREG_TMP1
14366					} else if (next_opline) {
14367						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >3, ZREG_TMP1
14368					} else {
14369						|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, =>default_b, ZREG_TMP1
14370					}
14371				}
14372				|	// zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
14373				|	SET_EX_OPLINE opline, REG0
14374				|	LOAD_32BIT_VAL FCARG1w, opline->op1.var
14375				|	EXT_CALL zend_jit_undefined_op_helper, REG0
14376				if (!zend_jit_check_exception_undef_result(Dst, opline)) {
14377					return 0;
14378				}
14379			}
14380			if (default_label) {
14381				|	b &default_label
14382			} else if (next_opline) {
14383				|	b >3
14384			} else {
14385				|	b =>default_b
14386			}
14387			|3:
14388		} else {
14389			ZEND_UNREACHABLE();
14390		}
14391	}
14392	return 1;
14393}
14394
14395static bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info)
14396{
14397	zend_arg_info *arg_info = &op_array->arg_info[-1];
14398	ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
14399	zend_jit_addr op1_addr = OP1_ADDR();
14400	bool needs_slow_check = 1;
14401	bool slow_check_in_cold = 1;
14402	uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
14403
14404	if (type_mask == 0) {
14405		slow_check_in_cold = 0;
14406	} else {
14407		if (((op1_info & MAY_BE_ANY) & type_mask) == 0) {
14408			slow_check_in_cold = 0;
14409		} else if (((op1_info & MAY_BE_ANY) | type_mask) == type_mask) {
14410			needs_slow_check = 0;
14411		} else if (is_power_of_two(type_mask)) {
14412			uint32_t type_code = concrete_type(type_mask);
14413			|	IF_NOT_ZVAL_TYPE op1_addr, type_code, >6, ZREG_TMP1
14414		} else {
14415			|	mov REG2w, #1
14416			|	GET_ZVAL_TYPE REG1w, op1_addr, TMP1
14417			|	lsl REG2w, REG2w, REG1w
14418			|	TST_32_WITH_CONST REG2w, type_mask, TMP1w
14419			|	beq >6
14420		}
14421	}
14422	if (needs_slow_check) {
14423		if (slow_check_in_cold) {
14424			|.cold_code
14425			|6:
14426		}
14427		|	SET_EX_OPLINE opline, REG1
14428		if (op1_info & MAY_BE_UNDEF) {
14429			|	IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >7, ZREG_TMP1
14430			|	LOAD_32BIT_VAL FCARG1x, opline->op1.var
14431			|	EXT_CALL zend_jit_undefined_op_helper, REG0
14432			|	cbz RETVALx, ->exception_handler
14433			|	LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
14434			|	b >8
14435		}
14436		|7:
14437		|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
14438		|8:
14439		|	ldr FCARG2x, EX->func
14440		|	LOAD_ADDR CARG3, (ptrdiff_t)arg_info
14441		|	ldr REG0, EX->run_time_cache
14442		|	ADD_SUB_64_WITH_CONST_32 add, CARG4, REG0, opline->op2.num, TMP1
14443		|	EXT_CALL zend_jit_verify_return_slow, REG0
14444		if (!zend_jit_check_exception(Dst)) {
14445			return 0;
14446		}
14447		if (slow_check_in_cold) {
14448			|	b >9
14449			|.code
14450		}
14451	}
14452	|9:
14453	return 1;
14454}
14455
14456static 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)
14457{
14458	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
14459
14460	// TODO: support for empty() ???
14461	ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY));
14462
14463	if (op1_info & MAY_BE_REF) {
14464		if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) {
14465			|	LOAD_ZVAL_ADDR FCARG1x, op1_addr
14466			op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14467		}
14468		|	ZVAL_DEREF FCARG1x, op1_info, TMP1w
14469		|1:
14470	}
14471
14472	if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) {
14473		if (exit_addr) {
14474			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPZ);
14475		} else if (smart_branch_opcode) {
14476			if (smart_branch_opcode == ZEND_JMPNZ) {
14477				|	b =>target_label
14478			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
14479				|	b =>target_label2
14480			}
14481		} else {
14482			|	SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2
14483		}
14484	} else if (!(op1_info & (MAY_BE_ANY - MAY_BE_NULL))) {
14485		if (exit_addr) {
14486			ZEND_ASSERT(smart_branch_opcode == ZEND_JMPNZ);
14487		} else if (smart_branch_opcode) {
14488			if (smart_branch_opcode != ZEND_JMPNZ) {
14489				|	b =>target_label
14490			}
14491		} else {
14492			|	SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2
14493		}
14494	} else {
14495		ZEND_ASSERT(Z_MODE(op1_addr) == IS_MEM_ZVAL);
14496		|	MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP1w, Rx(Z_REG(op1_addr)), (Z_OFFSET(op1_addr)+offsetof(zval, u1.v.type)), TMP1
14497		|	cmp TMP1w, #IS_NULL
14498		if (exit_addr) {
14499			if (smart_branch_opcode == ZEND_JMPNZ) {
14500				|	bgt &exit_addr
14501			} else {
14502				|	ble &exit_addr
14503			}
14504		} else if (smart_branch_opcode) {
14505			if (smart_branch_opcode == ZEND_JMPZ) {
14506				|	ble =>target_label
14507			} else if (smart_branch_opcode == ZEND_JMPNZ) {
14508				|	bgt =>target_label
14509			} else if (smart_branch_opcode == ZEND_JMPZNZ) {
14510				|	ble =>target_label
14511				|	b =>target_label2
14512			} else {
14513				ZEND_UNREACHABLE();
14514			}
14515		} else {
14516			|	cset REG0w, gt
14517			|	add REG0w, REG0w, #IS_FALSE
14518			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
14519		}
14520	}
14521
14522	return 1;
14523}
14524
14525static int zend_jit_fe_reset(dasm_State **Dst, const zend_op *opline, uint32_t op1_info)
14526{
14527	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
14528
14529	if (opline->op1_type == IS_CONST) {
14530		zval *zv = RT_CONSTANT(opline, opline->op1);
14531
14532		|	ZVAL_COPY_CONST res_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0
14533		if (Z_REFCOUNTED_P(zv)) {
14534			|	ADDREF_CONST zv, REG0, TMP1
14535		}
14536	} else {
14537		zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
14538
14539		|	// ZVAL_COPY(res, value);
14540		|	ZVAL_COPY_VALUE res_addr, -1, op1_addr, op1_info, ZREG_REG0, ZREG_FCARG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
14541		if (opline->op1_type == IS_CV) {
14542			|	TRY_ADDREF op1_info, REG0w, FCARG1x, TMP1w
14543		}
14544	}
14545	|	// Z_FE_POS_P(res) = 0;
14546	|	MEM_ACCESS_32_WITH_UOFFSET str, wzr, FP, (opline->result.var + offsetof(zval, u2.fe_pos)), TMP1
14547
14548	return 1;
14549}
14550
14551static 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)
14552{
14553	zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
14554
14555	if (!MAY_BE_HASH(op1_info) && !MAY_BE_PACKED(op1_info)) {
14556		/* empty array */
14557		if (exit_addr) {
14558			if (exit_opcode == ZEND_JMP) {
14559				|	b &exit_addr
14560			}
14561		} else {
14562			|	b =>target_label
14563		}
14564		return 1;
14565	}
14566
14567	|	// array = EX_VAR(opline->op1.var);
14568	|	// fe_ht = Z_ARRVAL_P(array);
14569	|	GET_ZVAL_PTR FCARG1x, op1_addr, TMP1
14570	|	// pos = Z_FE_POS_P(array);
14571	|	MEM_ACCESS_32_WITH_UOFFSET ldr, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1
14572	|	// p = fe_ht->arData + pos;
14573	||	ZEND_ASSERT(sizeof(Bucket) == 32);
14574	|	mov FCARG2w, REG0w
14575	|	ldr TMP1, [FCARG1x, #offsetof(zend_array, arData)]
14576	|	add FCARG2x, TMP1, FCARG2x, lsl #5
14577	|1:
14578	|	// if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
14579	|	ldr TMP1w, [FCARG1x, #offsetof(zend_array, nNumUsed)]
14580	|	cmp TMP1w, REG0w
14581	|	// ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
14582	|	// ZEND_VM_CONTINUE();
14583	if (exit_addr) {
14584		if (exit_opcode == ZEND_JMP) {
14585			|	bls &exit_addr
14586		} else {
14587			|	bls >3
14588		}
14589	} else {
14590		|	bls =>target_label
14591	}
14592	|	// pos++;
14593	|	add REG0w, REG0w, #1
14594	|	// value_type = Z_TYPE_INFO_P(value);
14595	|	// if (EXPECTED(value_type != IS_UNDEF)) {
14596	if (!exit_addr || exit_opcode == ZEND_JMP) {
14597		|	IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, >3, TMP1w
14598	} else {
14599		|	IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, &exit_addr, TMP1w
14600	}
14601	|	// p++;
14602	|	add FCARG2x, FCARG2x, #sizeof(Bucket)
14603	|	b <1
14604	|3:
14605
14606	if (!exit_addr || exit_opcode == ZEND_JMP) {
14607		zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0);
14608		zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
14609		uint32_t val_info;
14610
14611		|	// Z_FE_POS_P(array) = pos + 1;
14612		|	MEM_ACCESS_32_WITH_UOFFSET str, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1
14613
14614		if (RETURN_VALUE_USED(opline)) {
14615			zend_jit_addr res_addr = RES_ADDR();
14616
14617			if ((op1_info & MAY_BE_ARRAY_KEY_LONG)
14618			 && (op1_info & MAY_BE_ARRAY_KEY_STRING)) {
14619				|	// if (!p->key) {
14620				|	ldr REG0, [FCARG2x, #offsetof(Bucket, key)]
14621				|	cbz REG0, >2
14622			}
14623			if (op1_info & MAY_BE_ARRAY_KEY_STRING) {
14624				|	// ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key);
14625				|	ldr REG0, [FCARG2x, #offsetof(Bucket, key)]
14626				|	SET_ZVAL_PTR res_addr, REG0, TMP1
14627				|	ldr TMP1w, [REG0, #offsetof(zend_refcounted, gc.u.type_info)]
14628				|	TST_32_WITH_CONST TMP1w, IS_STR_INTERNED, TMP2w
14629				|	beq >1
14630				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING, TMP1w, TMP2
14631				|	b >3
14632				|1:
14633				|	GC_ADDREF REG0, TMP1w
14634				|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX, TMP1w, TMP2
14635
14636				if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
14637				    |	b >3
14638					|2:
14639				}
14640			}
14641			if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
14642				|	// ZVAL_LONG(EX_VAR(opline->result.var), p->h);
14643				|	ldr REG0, [FCARG2x, #offsetof(Bucket, h)]
14644				|	SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1
14645				|	SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2
14646			}
14647			|3:
14648		}
14649
14650		val_info = ((op1_info & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
14651		if (val_info & MAY_BE_ARRAY) {
14652			val_info |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
14653		}
14654		if (op1_info & MAY_BE_ARRAY_OF_REF) {
14655			val_info |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY |
14656				MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
14657		} else if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
14658			val_info |= MAY_BE_RC1 | MAY_BE_RCN;
14659		}
14660
14661		if (opline->op2_type == IS_CV) {
14662			|	// zend_assign_to_variable(variable_ptr, value, IS_CV, EX_USES_STRICT_TYPES());
14663			if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, op2_info, -1, IS_CV, val_addr, val_info, 0, 1)) {
14664				return 0;
14665			}
14666		} else {
14667			|	// ZVAL_COPY(res, value);
14668			|	ZVAL_COPY_VALUE var_addr, -1, val_addr, val_info, ZREG_REG0, ZREG_FCARG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
14669			|	TRY_ADDREF val_info, REG0w, FCARG1x, TMP1w
14670		}
14671	}
14672
14673	return 1;
14674}
14675
14676static int zend_jit_fetch_constant(dasm_State          **Dst,
14677                                   const zend_op        *opline,
14678                                   const zend_op_array  *op_array,
14679                                   zend_ssa             *ssa,
14680                                   const zend_ssa_op    *ssa_op,
14681                                   zend_jit_addr         res_addr)
14682{
14683	zval *zv = RT_CONSTANT(opline, opline->op2) + 1;
14684	zend_jit_addr const_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0);
14685	uint32_t res_info = RES_INFO();
14686
14687	|	// c = CACHED_PTR(opline->extended_value);
14688	|	ldr FCARG1x, EX->run_time_cache
14689	|	MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, FCARG1x, opline->extended_value, TMP1
14690	|	// if (c != NULL)
14691	|	cbz REG0, >9
14692	if (!zend_jit_is_persistent_constant(zv, opline->op1.num)) {
14693		|	// if (!IS_SPECIAL_CACHE_VAL(c))
14694		||	ZEND_ASSERT(CACHE_SPECIAL == 1);
14695		|	TST_64_WITH_ONE REG0
14696		|	bne >9
14697	}
14698	|8:
14699
14700	if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) {
14701		zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
14702		uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
14703		int32_t exit_point;
14704		const void *exit_addr = NULL;
14705
14706		SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
14707		SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0);
14708		exit_point = zend_jit_trace_get_exit_point(opline+1, 0);
14709		SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
14710		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14711		if (!exit_addr) {
14712			return 0;
14713		}
14714		res_info &= ~MAY_BE_GUARD;
14715		ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
14716
14717		uint32_t type = concrete_type(res_info);
14718
14719		if (type < IS_STRING) {
14720			|	IF_NOT_ZVAL_TYPE const_addr, type, &exit_addr, ZREG_TMP1
14721		} else {
14722			|	GET_ZVAL_TYPE_INFO REG2w, const_addr, TMP1
14723			|	IF_NOT_TYPE REG2w, type, &exit_addr
14724		}
14725		|	ZVAL_COPY_VALUE_V res_addr, -1, const_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0
14726		if (type < IS_STRING) {
14727			if (Z_MODE(res_addr) == IS_MEM_ZVAL) {
14728				|	SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2
14729			} else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) {
14730				return 0;
14731			}
14732		} else {
14733			|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1
14734			|	TRY_ADDREF res_info, REG2w, REG1, TMP1w
14735		}
14736	} else {
14737		|	ZVAL_COPY_VALUE res_addr, MAY_BE_ANY, const_addr, MAY_BE_ANY, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0
14738		|	TRY_ADDREF MAY_BE_ANY, REG0w, REG1, TMP1w
14739	}
14740
14741	|.cold_code
14742	|9:
14743	|	// SAVE_OPLINE();
14744	|	SET_EX_OPLINE opline, REG0
14745	|	// zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num OPLINE_CC EXECUTE_DATA_CC);
14746	|	LOAD_ADDR FCARG1x, zv
14747	|	LOAD_32BIT_VAL FCARG2w, opline->op1.num
14748	|	EXT_CALL zend_jit_get_constant, REG0
14749	|	mov REG0, RETVALx
14750	|	// ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
14751	|	cbnz REG0, <8
14752	|	b ->exception_handler
14753	|.code
14754	return 1;
14755}
14756
14757static 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)
14758{
14759	HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
14760	zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
14761
14762	ZEND_ASSERT(opline->op1_type != IS_VAR && opline->op1_type != IS_TMP_VAR);
14763	ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_STRING);
14764
14765	|	// result = zend_hash_find_ex(ht, Z_STR_P(op1), OP1_TYPE == IS_CONST);
14766	|	LOAD_ADDR FCARG1x, ht
14767	if (opline->op1_type != IS_CONST) {
14768		|	GET_ZVAL_PTR FCARG2x, op1_addr, TMP1
14769		|	EXT_CALL zend_hash_find, REG0
14770	} else {
14771		zend_string *str = Z_STR_P(RT_CONSTANT(opline, opline->op1));
14772		|	LOAD_ADDR FCARG2x, str
14773		|	EXT_CALL zend_hash_find_known_hash, REG0
14774	}
14775	if (exit_addr) {
14776		if (smart_branch_opcode == ZEND_JMPZ) {
14777			|	cbz RETVALx, &exit_addr
14778		} else {
14779			|	cbnz RETVALx, &exit_addr
14780		}
14781	} else if (smart_branch_opcode) {
14782		if (smart_branch_opcode == ZEND_JMPZ) {
14783			|	cbz RETVALx, =>target_label
14784		} else if (smart_branch_opcode == ZEND_JMPNZ) {
14785			|	cbnz RETVALx, =>target_label
14786		} else if (smart_branch_opcode == ZEND_JMPZNZ) {
14787			|	cbz RETVALx, =>target_label
14788			|	b =>target_label2
14789		} else {
14790			ZEND_UNREACHABLE();
14791		}
14792	} else {
14793		|	tst RETVALx, RETVALx
14794		|	cset REG0w, ne
14795		|	add REG0w, REG0w, #IS_FALSE
14796		|	SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
14797	}
14798
14799	return 1;
14800}
14801
14802static int zend_jit_rope(dasm_State **Dst, const zend_op *opline, uint32_t op2_info)
14803{
14804	uint32_t offset;
14805
14806	offset = (opline->opcode == ZEND_ROPE_INIT) ?
14807		opline->result.var :
14808		opline->op1.var + opline->extended_value * sizeof(zend_string*);
14809
14810	if (opline->op2_type == IS_CONST) {
14811		zval *zv = RT_CONSTANT(opline, opline->op2);
14812		zend_string *str;
14813
14814		ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
14815		str = Z_STR_P(zv);
14816		|	LOAD_ADDR REG0, str
14817		|	MEM_ACCESS_64_WITH_UOFFSET str, REG0, FP, offset, TMP1
14818	} else {
14819		zend_jit_addr op2_addr = OP2_ADDR();
14820
14821		ZEND_ASSERT((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING);
14822
14823		|	GET_ZVAL_PTR REG1, op2_addr, TMP1
14824		|	MEM_ACCESS_64_WITH_UOFFSET str, REG1, FP, offset, TMP1
14825		if (opline->op2_type == IS_CV) {
14826			|	GET_ZVAL_TYPE_INFO REG0w, op2_addr, TMP1
14827			|	TRY_ADDREF op2_info, REG0w, REG1, TMP1w
14828		}
14829	}
14830
14831	if (opline->opcode == ZEND_ROPE_END) {
14832		zend_jit_addr res_addr = RES_ADDR();
14833
14834		|	ADD_SUB_64_WITH_CONST add, FCARG1x, FP, opline->op1.var, TMP1
14835		|	LOAD_32BIT_VAL FCARG2w, opline->extended_value
14836		|	EXT_CALL zend_jit_rope_end, TMP1
14837		|	SET_ZVAL_PTR res_addr, RETVALx, TMP1
14838		|	SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX, TMP1w, TMP2
14839	}
14840
14841	return 1;
14842}
14843
14844static bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_addr)
14845{
14846	int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
14847	const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14848
14849	if (!exit_addr) {
14850		return 0;
14851	}
14852	|	IF_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr, ZREG_TMP1
14853
14854	return 1;
14855}
14856
14857static 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)
14858{
14859	zend_jit_addr var_addr = *var_addr_ptr;
14860	uint32_t var_info = *var_info_ptr;
14861	const void *exit_addr = NULL;
14862
14863	if (add_ref_guard || add_type_guard) {
14864		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
14865
14866		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14867		if (!exit_addr) {
14868			return 0;
14869		}
14870	}
14871
14872	if (add_ref_guard) {
14873		|	IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr, ZREG_TMP1
14874	}
14875	if (opline->opcode == ZEND_INIT_METHOD_CALL && opline->op1_type == IS_VAR) {
14876		/* Hack: Convert reference to regular value to simplify JIT code for INIT_METHOD_CALL */
14877		if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) {
14878			|	LOAD_ZVAL_ADDR FCARG1x, var_addr
14879		}
14880		|	EXT_CALL zend_jit_unref_helper, REG0
14881	} else {
14882		|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
14883		var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, offsetof(zend_reference, val));
14884		*var_addr_ptr = var_addr;
14885	}
14886
14887	if (var_type != IS_UNKNOWN) {
14888		var_type &= ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED);
14889	}
14890	if (add_type_guard
14891	 && var_type != IS_UNKNOWN
14892	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
14893		|	IF_NOT_ZVAL_TYPE var_addr, var_type, &exit_addr, ZREG_TMP1
14894
14895		ZEND_ASSERT(var_info & (1 << var_type));
14896		if (var_type < IS_STRING) {
14897			var_info = (1 << var_type);
14898		} else if (var_type != IS_ARRAY) {
14899			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
14900		} else {
14901			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));
14902		}
14903
14904		*var_info_ptr = var_info;
14905	} else {
14906		var_info &= ~MAY_BE_REF;
14907		*var_info_ptr = var_info;
14908	}
14909	*var_info_ptr |= MAY_BE_GUARD; /* prevent generation of specialized zval dtor */
14910
14911	return 1;
14912}
14913
14914static 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)
14915{
14916	zend_jit_addr var_addr = *var_addr_ptr;
14917	uint32_t var_info = *var_info_ptr;
14918	int32_t exit_point;
14919	const void *exit_addr;
14920
14921	if (add_indirect_guard) {
14922		int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
14923		const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14924
14925		if (!exit_addr) {
14926			return 0;
14927		}
14928		|	IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr, ZREG_TMP1
14929		|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
14930	} else {
14931		/* May be already loaded into FCARG1a or RAX by previus FETCH_OBJ_W/DIM_W */
14932		if (opline->op1_type != IS_VAR ||
14933				(opline-1)->result_type != IS_VAR  ||
14934				(opline-1)->result.var != opline->op1.var ||
14935				(opline-1)->op1_type == IS_VAR ||
14936				(opline-1)->op2_type == IS_VAR ||
14937				(opline-1)->op2_type == IS_TMP_VAR) {
14938			|	GET_ZVAL_PTR FCARG1x, var_addr, TMP1
14939		} else if ((opline-1)->opcode == ZEND_FETCH_DIM_W || (opline-1)->opcode == ZEND_FETCH_DIM_RW) {
14940			|	mov FCARG1x, REG0
14941		}
14942	}
14943	*var_info_ptr &= ~MAY_BE_INDIRECT;
14944	var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0);
14945	*var_addr_ptr = var_addr;
14946
14947	if (var_type != IS_UNKNOWN) {
14948		var_type &= ~(IS_TRACE_INDIRECT|IS_TRACE_PACKED);
14949	}
14950	if (!(var_type & IS_TRACE_REFERENCE)
14951	 && var_type != IS_UNKNOWN
14952	 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) {
14953		exit_point = zend_jit_trace_get_exit_point(opline, 0);
14954		exit_addr = zend_jit_trace_get_exit_addr(exit_point);
14955
14956		if (!exit_addr) {
14957			return 0;
14958		}
14959
14960		|	IF_NOT_Z_TYPE FCARG1x, var_type, &exit_addr, TMP1w
14961
14962		//var_info = zend_jit_trace_type_to_info_ex(var_type, var_info);
14963		ZEND_ASSERT(var_info & (1 << var_type));
14964		if (var_type < IS_STRING) {
14965			var_info = (1 << var_type);
14966		} else if (var_type != IS_ARRAY) {
14967			var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN));
14968		} else {
14969			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));
14970		}
14971
14972		*var_info_ptr = var_info;
14973	}
14974
14975	return 1;
14976}
14977
14978static 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)
14979{
14980	if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) {
14981		return 0;
14982	}
14983
14984	switch (opline->opcode) {
14985		case ZEND_QM_ASSIGN:
14986		case ZEND_SEND_VAR:
14987		case ZEND_ASSIGN:
14988		case ZEND_PRE_INC:
14989		case ZEND_PRE_DEC:
14990		case ZEND_POST_INC:
14991		case ZEND_POST_DEC:
14992			return 1;
14993		case ZEND_ADD:
14994		case ZEND_SUB:
14995		case ZEND_MUL:
14996		case ZEND_BW_OR:
14997		case ZEND_BW_AND:
14998		case ZEND_BW_XOR:
14999		case ZEND_SL:
15000		case ZEND_SR:
15001			if (def_var == ssa_op->result_def &&
15002			    use_var == ssa_op->op1_use) {
15003				return 1;
15004			}
15005			break;
15006		default:
15007			break;
15008	}
15009	return 0;
15010}
15011
15012static 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)
15013{
15014	uint32_t op1_info, op2_info;
15015
15016	switch (opline->opcode) {
15017		case ZEND_SEND_VAR:
15018		case ZEND_SEND_VAL:
15019		case ZEND_SEND_VAL_EX:
15020			return (opline->op2_type != IS_CONST);
15021		case ZEND_QM_ASSIGN:
15022		case ZEND_IS_SMALLER:
15023		case ZEND_IS_SMALLER_OR_EQUAL:
15024		case ZEND_IS_EQUAL:
15025		case ZEND_IS_NOT_EQUAL:
15026		case ZEND_IS_IDENTICAL:
15027		case ZEND_IS_NOT_IDENTICAL:
15028		case ZEND_CASE:
15029			return 1;
15030		case ZEND_RETURN:
15031			return (op_array->type != ZEND_EVAL_CODE && op_array->function_name);
15032		case ZEND_ASSIGN:
15033			op1_info = OP1_INFO();
15034			op2_info = OP2_INFO();
15035			return
15036				opline->op1_type == IS_CV &&
15037				!(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_RESOURCE|MAY_BE_OBJECT|MAY_BE_REF)) &&
15038				!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)));
15039		case ZEND_ADD:
15040		case ZEND_SUB:
15041		case ZEND_MUL:
15042			op1_info = OP1_INFO();
15043			op2_info = OP2_INFO();
15044			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE)));
15045		case ZEND_BW_OR:
15046		case ZEND_BW_AND:
15047		case ZEND_BW_XOR:
15048		case ZEND_SL:
15049		case ZEND_SR:
15050		case ZEND_MOD:
15051			op1_info = OP1_INFO();
15052			op2_info = OP2_INFO();
15053			return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG));
15054		case ZEND_PRE_INC:
15055		case ZEND_PRE_DEC:
15056		case ZEND_POST_INC:
15057		case ZEND_POST_DEC:
15058			op1_info = OP1_INFO();
15059			op2_info = OP1_DEF_INFO();
15060			return opline->op1_type == IS_CV
15061				&& !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG))
15062				&& (op2_info & MAY_BE_LONG);
15063		case ZEND_STRLEN:
15064			op1_info = OP1_INFO();
15065			return (opline->op1_type & (IS_CV|IS_CONST))
15066				&& (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_STRING;
15067		case ZEND_COUNT:
15068			op1_info = OP1_INFO();
15069			return (opline->op1_type & (IS_CV|IS_CONST))
15070				&& (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_ARRAY;
15071		case ZEND_JMPZ:
15072		case ZEND_JMPNZ:
15073			if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
15074				if (!ssa->cfg.map) {
15075					return 0;
15076				}
15077				if (opline > op_array->opcodes + ssa->cfg.blocks[ssa->cfg.map[opline-op_array->opcodes]].start &&
15078				    ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
15079					return 0;
15080				}
15081			}
15082			ZEND_FALLTHROUGH;
15083		case ZEND_BOOL:
15084		case ZEND_BOOL_NOT:
15085		case ZEND_JMPZNZ:
15086		case ZEND_JMPZ_EX:
15087		case ZEND_JMPNZ_EX:
15088			return 1;
15089		case ZEND_FETCH_CONSTANT:
15090			return 1;
15091		case ZEND_FETCH_DIM_R:
15092			op1_info = OP1_INFO();
15093			op2_info = OP2_INFO();
15094			if (trace
15095			 && trace->op1_type != IS_UNKNOWN
15096			 && (trace->op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)) == IS_ARRAY) {
15097				op1_info &= ~((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY);
15098			}
15099			return ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) &&
15100				(!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || !(op1_info & MAY_BE_RC1)) &&
15101					(((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) ||
15102					 (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) &&
15103						 (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & MAY_BE_RC1))));
15104	}
15105	return 0;
15106}
15107
15108static bool zend_jit_var_supports_reg(zend_ssa *ssa, int var)
15109{
15110	if (ssa->vars[var].no_val) {
15111		/* we don't need the value */
15112		return 0;
15113	}
15114
15115	if (!(JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL)) {
15116		/* Disable global register allocation,
15117		 * register allocation for SSA variables connected through Phi functions
15118		 */
15119		if (ssa->vars[var].definition_phi) {
15120			return 0;
15121		}
15122		if (ssa->vars[var].phi_use_chain) {
15123			zend_ssa_phi *phi = ssa->vars[var].phi_use_chain;
15124			do {
15125				if (!ssa->vars[phi->ssa_var].no_val) {
15126					return 0;
15127				}
15128				phi = zend_ssa_next_use_phi(ssa, var, phi);
15129			} while (phi);
15130		}
15131	}
15132
15133	if (((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_DOUBLE) &&
15134	    ((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_LONG)) {
15135	    /* bad type */
15136		return 0;
15137	}
15138
15139	return 1;
15140}
15141
15142static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, int var)
15143{
15144	if (!zend_jit_var_supports_reg(ssa, var)) {
15145		return 0;
15146	}
15147
15148	if (ssa->vars[var].definition >= 0) {
15149		uint32_t def = ssa->vars[var].definition;
15150		if (!zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + def, ssa->ops + def, NULL)) {
15151			return 0;
15152		}
15153	}
15154
15155	if (ssa->vars[var].use_chain >= 0) {
15156		int use = ssa->vars[var].use_chain;
15157
15158		do {
15159			if (!zend_ssa_is_no_val_use(op_array->opcodes + use, ssa->ops + use, var) &&
15160			    !zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + use, ssa->ops + use, NULL)) {
15161				return 0;
15162			}
15163			use = zend_ssa_next_use(ssa->ops, var, use);
15164		} while (use >= 0);
15165	}
15166
15167	return 1;
15168}
15169
15170static 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)
15171{
15172	uint32_t op1_info, op2_info;
15173
15174	switch (opline->opcode) {
15175		case ZEND_FETCH_DIM_R:
15176			op1_info = OP1_INFO();
15177			op2_info = OP2_INFO();
15178			if (((opline->op1_type & (IS_TMP_VAR|IS_VAR)) &&
15179			     (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) ||
15180			    ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) &&
15181			     (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)))) {
15182				return ZEND_REGSET(ZREG_FCARG1);
15183			}
15184			break;
15185		default:
15186			break;
15187	}
15188
15189	return ZEND_REGSET_EMPTY;
15190}
15191
15192static 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)
15193{
15194	uint32_t op1_info, op2_info, res_info;
15195	zend_regset regset = ZEND_REGSET_SCRATCH;
15196
15197	switch (opline->opcode) {
15198		case ZEND_NOP:
15199		case ZEND_OP_DATA:
15200		case ZEND_JMP:
15201		case ZEND_RETURN:
15202			regset = ZEND_REGSET_EMPTY;
15203			break;
15204		case ZEND_QM_ASSIGN:
15205			if (ssa_op->op1_def == current_var ||
15206			    ssa_op->result_def == current_var) {
15207				regset = ZEND_REGSET_EMPTY;
15208				break;
15209			}
15210			/* break missing intentionally */
15211		case ZEND_SEND_VAL:
15212		case ZEND_SEND_VAL_EX:
15213			if (opline->op2_type == IS_CONST) {
15214				break;
15215			}
15216			if (ssa_op->op1_use == current_var) {
15217				regset = ZEND_REGSET(ZREG_REG0);
15218				break;
15219			}
15220			op1_info = OP1_INFO();
15221			if (!(op1_info & MAY_BE_UNDEF)) {
15222				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
15223					regset = ZEND_REGSET(ZREG_FPR0);
15224				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
15225					regset = ZEND_REGSET(ZREG_REG0);
15226				} else {
15227					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2));
15228				}
15229			}
15230			break;
15231		case ZEND_SEND_VAR:
15232			if (opline->op2_type == IS_CONST) {
15233				break;
15234			}
15235			if (ssa_op->op1_use == current_var ||
15236			    ssa_op->op1_def == current_var) {
15237				regset = ZEND_REGSET_EMPTY;
15238				break;
15239			}
15240			op1_info = OP1_INFO();
15241			if (!(op1_info & MAY_BE_UNDEF)) {
15242				if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
15243					regset = ZEND_REGSET(ZREG_FPR0);
15244				} else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
15245				} else {
15246					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2));
15247					if (op1_info & MAY_BE_REF) {
15248						ZEND_REGSET_INCL(regset, ZREG_REG1);
15249					}
15250				}
15251			}
15252			break;
15253		case ZEND_ASSIGN:
15254			if (ssa_op->op2_use == current_var ||
15255			    ssa_op->op2_def == current_var ||
15256			    ssa_op->op1_def == current_var ||
15257			    ssa_op->result_def == current_var) {
15258				regset = ZEND_REGSET_EMPTY;
15259				break;
15260			}
15261			op1_info = OP1_INFO();
15262			op2_info = OP2_INFO();
15263			if (opline->op1_type == IS_CV
15264			 && !(op2_info & MAY_BE_UNDEF)
15265			 && !(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
15266				if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) {
15267					regset = ZEND_REGSET(ZREG_FPR0);
15268				} else if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) {
15269					regset = ZEND_REGSET(ZREG_REG0);
15270				} else {
15271					regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2));
15272				}
15273			}
15274			break;
15275		case ZEND_PRE_INC:
15276		case ZEND_PRE_DEC:
15277		case ZEND_POST_INC:
15278		case ZEND_POST_DEC:
15279			if (ssa_op->op1_use == current_var ||
15280			    ssa_op->op1_def == current_var ||
15281			    ssa_op->result_def == current_var) {
15282				regset = ZEND_REGSET_EMPTY;
15283				break;
15284			}
15285			op1_info = OP1_INFO();
15286			if (opline->op1_type == IS_CV
15287			 && (op1_info & MAY_BE_LONG)
15288			 && !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
15289				regset = ZEND_REGSET_EMPTY;
15290				if (op1_info & MAY_BE_DOUBLE) {
15291					regset = ZEND_REGSET(ZREG_FPR0);
15292				}
15293				if (opline->result_type != IS_UNUSED && (op1_info & MAY_BE_LONG)) {
15294					ZEND_REGSET_INCL(regset, ZREG_REG1);
15295				}
15296			}
15297			break;
15298		case ZEND_ADD:
15299		case ZEND_SUB:
15300		case ZEND_MUL:
15301			op1_info = OP1_INFO();
15302			op2_info = OP2_INFO();
15303			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
15304			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
15305
15306				regset = ZEND_REGSET_EMPTY;
15307				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
15308					if (ssa_op->result_def != current_var &&
15309					    (ssa_op->op1_use != current_var || !last_use)) {
15310						ZEND_REGSET_INCL(regset, ZREG_REG0);
15311					}
15312					res_info = RES_INFO();
15313					if (res_info & MAY_BE_DOUBLE) {
15314						ZEND_REGSET_INCL(regset, ZREG_REG0);
15315						ZEND_REGSET_INCL(regset, ZREG_FPR0);
15316						ZEND_REGSET_INCL(regset, ZREG_FPR1);
15317					} else if (res_info & MAY_BE_GUARD) {
15318						ZEND_REGSET_INCL(regset, ZREG_REG0);
15319					}
15320				}
15321				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
15322					if (ssa_op->result_def != current_var) {
15323						ZEND_REGSET_INCL(regset, ZREG_FPR0);
15324					}
15325				}
15326				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
15327					if (zend_is_commutative(opline->opcode)) {
15328						if (ssa_op->result_def != current_var) {
15329							ZEND_REGSET_INCL(regset, ZREG_FPR0);
15330						}
15331					} else {
15332						ZEND_REGSET_INCL(regset, ZREG_FPR0);
15333						if (ssa_op->result_def != current_var &&
15334						    (ssa_op->op1_use != current_var || !last_use)) {
15335							ZEND_REGSET_INCL(regset, ZREG_FPR1);
15336						}
15337					}
15338				}
15339				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
15340					if (ssa_op->result_def != current_var &&
15341					    (ssa_op->op1_use != current_var || !last_use) &&
15342					    (!zend_is_commutative(opline->opcode) || ssa_op->op2_use != current_var || !last_use)) {
15343						ZEND_REGSET_INCL(regset, ZREG_FPR0);
15344					}
15345				}
15346			}
15347			break;
15348		case ZEND_BW_OR:
15349		case ZEND_BW_AND:
15350		case ZEND_BW_XOR:
15351		case ZEND_SL:
15352		case ZEND_SR:
15353		case ZEND_MOD:
15354			op1_info = OP1_INFO();
15355			op2_info = OP2_INFO();
15356			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) &&
15357			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
15358				regset = ZEND_REGSET_EMPTY;
15359				if (ssa_op->result_def != current_var &&
15360				    (ssa_op->op1_use != current_var || !last_use)) {
15361					ZEND_REGSET_INCL(regset, ZREG_REG0);
15362				}
15363			}
15364			break;
15365		case ZEND_IS_SMALLER:
15366		case ZEND_IS_SMALLER_OR_EQUAL:
15367		case ZEND_IS_EQUAL:
15368		case ZEND_IS_NOT_EQUAL:
15369		case ZEND_IS_IDENTICAL:
15370		case ZEND_IS_NOT_IDENTICAL:
15371		case ZEND_CASE:
15372			op1_info = OP1_INFO();
15373			op2_info = OP2_INFO();
15374			if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) &&
15375			    !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) {
15376				regset = ZEND_REGSET_EMPTY;
15377				if (!(opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ))) {
15378					ZEND_REGSET_INCL(regset, ZREG_REG0);
15379				}
15380				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) &&
15381				    opline->op1_type != IS_CONST && opline->op2_type != IS_CONST) {
15382					if (ssa_op->op1_use != current_var &&
15383					    ssa_op->op2_use != current_var) {
15384						ZEND_REGSET_INCL(regset, ZREG_REG0);
15385					}
15386				}
15387				if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
15388					ZEND_REGSET_INCL(regset, ZREG_FPR0);
15389				}
15390				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
15391					ZEND_REGSET_INCL(regset, ZREG_FPR0);
15392				}
15393				if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
15394					if (ssa_op->op1_use != current_var &&
15395					    ssa_op->op2_use != current_var) {
15396						ZEND_REGSET_INCL(regset, ZREG_FPR0);
15397					}
15398				}
15399			}
15400			break;
15401		case ZEND_BOOL:
15402		case ZEND_BOOL_NOT:
15403		case ZEND_JMPZ:
15404		case ZEND_JMPNZ:
15405		case ZEND_JMPZNZ:
15406		case ZEND_JMPZ_EX:
15407		case ZEND_JMPNZ_EX:
15408			op1_info = OP1_INFO();
15409			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)))) {
15410				regset = ZEND_REGSET_EMPTY;
15411				if (op1_info & MAY_BE_DOUBLE) {
15412					ZEND_REGSET_INCL(regset, ZREG_FPR0);
15413				}
15414				if (opline->opcode == ZEND_BOOL ||
15415				    opline->opcode == ZEND_BOOL_NOT ||
15416				    opline->opcode == ZEND_JMPZ_EX ||
15417				    opline->opcode == ZEND_JMPNZ_EX) {
15418					ZEND_REGSET_INCL(regset, ZREG_REG0);
15419				}
15420			}
15421			break;
15422		case ZEND_DO_UCALL:
15423		case ZEND_DO_FCALL:
15424		case ZEND_DO_FCALL_BY_NAME:
15425		case ZEND_INCLUDE_OR_EVAL:
15426		case ZEND_GENERATOR_CREATE:
15427		case ZEND_YIELD:
15428		case ZEND_YIELD_FROM:
15429			regset = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP);
15430			break;
15431		default:
15432			break;
15433	}
15434
15435	if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
15436		if (ssa_op == ssa->ops
15437		 && JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].op == ZEND_JIT_TRACE_INIT_CALL
15438		 && (JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
15439			ZEND_REGSET_INCL(regset, ZREG_REG0);
15440			ZEND_REGSET_INCL(regset, ZREG_REG1);
15441		}
15442	}
15443
15444	return regset;
15445}
15446
15447static size_t dasm_venners_size = 0;
15448void **dasm_labels_veneers = NULL;
15449
15450static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int *b, uint32_t *cp, ptrdiff_t offset)
15451{
15452	void *veneer;
15453	ptrdiff_t na;
15454	int n, m;
15455
15456	/* try to reuse veneers for global labels */
15457	if ((ins >> 16) == DASM_REL_LG
15458	 && *(b-1) < 0
15459	 && dasm_labels_veneers[-*(b-1)]) {
15460
15461		veneer = dasm_labels_veneers[-*(b-1)];
15462		na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4;
15463		n = (int)na;
15464
15465		/* check if we can jump to veneer */
15466		if ((ptrdiff_t)n != na) {
15467			/* pass */
15468		} else if (!(ins & 0xf800)) {  /* B, BL */
15469			if ((n & 3) == 0 && ((n+0x08000000) >> 28) == 0) {
15470				return n;
15471			}
15472		} else if ((ins & 0x800)) {  /* B.cond, CBZ, CBNZ, LDR* literal */
15473			if ((n & 3) == 0 && ((n+0x00100000) >> 21) == 0) {
15474				return n;
15475			}
15476		} else if ((ins & 0x3000) == 0x2000) {  /* ADR */
15477			/* pass */
15478		} else if ((ins & 0x3000) == 0x3000) {  /* ADRP */
15479			/* pass */
15480		} else if ((ins & 0x1000)) {  /* TBZ, TBNZ */
15481			if ((n & 3) == 0 && ((n+0x00008000) >> 16) == 0) {
15482				return n;
15483			}
15484		}
15485	} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
15486	 && (ins >> 16) == DASM_REL_A) {
15487		ptrdiff_t addr = (((ptrdiff_t)(*(b-1))) << 32) | (unsigned int)(*(b-2));
15488
15489		if ((void*)addr >= dasm_buf && (void*)addr < dasm_end) {
15490			uint32_t exit_point = zend_jit_trace_find_exit_point((void*)addr);
15491			zend_jit_trace_info *t = zend_jit_get_current_trace_info();
15492
15493			if (exit_point != (uint32_t)-1) {
15494				/* Use exit points table */
15495
15496				ZEND_ASSERT(exit_point < t->exit_count);
15497
15498				veneer = (char*)buffer + dasm_getpclabel(&Dst, 1) - (t->exit_count - exit_point) * 4;
15499				na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4;
15500				n = (int)na;
15501
15502				/* check if we can jump to veneer */
15503				if ((ptrdiff_t)n != na) {
15504					ZEND_ASSERT(0);
15505					return 0;
15506				} else if (!(ins & 0xf800)) {  /* B, BL */
15507					if ((n & 3) != 0 || ((n+0x08000000) >> 28) != 0) {
15508						ZEND_ASSERT(0);
15509						return 0;
15510					}
15511				} else if ((ins & 0x800)) {  /* B.cond, CBZ, CBNZ, LDR* literal */
15512					if ((n & 3) != 0 || ((n+0x00100000) >> 21) != 0) {
15513						ZEND_ASSERT(0);
15514						return 0;
15515					}
15516				} else if ((ins & 0x3000) == 0x2000) {  /* ADR */
15517					ZEND_ASSERT(0);
15518					return 0;
15519				} else if ((ins & 0x3000) == 0x3000) {  /* ADRP */
15520					ZEND_ASSERT(0);
15521					return 0;
15522				} else if ((ins & 0x1000)) {  /* TBZ, TBNZ */
15523					if ((n & 3) != 0 || ((n+0x00008000) >> 16) != 0) {
15524						ZEND_ASSERT(0);
15525						return 0;
15526					}
15527				} else {
15528					ZEND_ASSERT(0);
15529					return 0;
15530				}
15531				return n;
15532			}
15533		}
15534	}
15535
15536	veneer = (char*)buffer + (Dst->codesize + dasm_venners_size);
15537
15538	if (veneer > dasm_end) {
15539		return 0; /* jit_buffer_size overflow */
15540	}
15541
15542	na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4;
15543	n = (int)na;
15544
15545	/* check if we can jump to veneer */
15546	if ((ptrdiff_t)n != na) {
15547		ZEND_ASSERT(0);
15548		return 0;
15549	} else if (!(ins & 0xf800)) {  /* B, BL */
15550		if ((n & 3) != 0 || ((n+0x08000000) >> 28) != 0) {
15551			ZEND_ASSERT(0);
15552			return 0;
15553		}
15554	} else if ((ins & 0x800)) {  /* B.cond, CBZ, CBNZ, LDR* literal */
15555		if ((n & 3) != 0 || ((n+0x00100000) >> 21) != 0) {
15556			ZEND_ASSERT(0);
15557			return 0;
15558		}
15559	} else if ((ins & 0x3000) == 0x2000) {  /* ADR */
15560		ZEND_ASSERT(0);
15561		return 0;
15562	} else if ((ins & 0x3000) == 0x3000) {  /* ADRP */
15563		ZEND_ASSERT(0);
15564		return 0;
15565	} else if ((ins & 0x1000)) {  /* TBZ, TBNZ */
15566		if ((n & 3) != 0 || ((n+0x00008000) >> 16) != 0) {
15567			ZEND_ASSERT(0);
15568			return 0;
15569		}
15570	} else if ((ins & 0x8000)) {  /* absolute */
15571		ZEND_ASSERT(0);
15572		return 0;
15573	} else {
15574		ZEND_ASSERT(0);
15575		return 0;
15576	}
15577
15578	// TODO: support for long veneers (above 128MB) ???
15579
15580	/* check if we can use B to jump from veneer */
15581	na = (ptrdiff_t)cp + offset - (ptrdiff_t)veneer - 4;
15582	m = (int)na;
15583	if ((ptrdiff_t)m != na) {
15584		ZEND_ASSERT(0);
15585		return 0;
15586	} else if ((m & 3) != 0 || ((m+0x08000000) >> 28) != 0) {
15587		ZEND_ASSERT(0);
15588		return 0;
15589	}
15590
15591	/* generate B instruction */
15592	*(uint32_t*)veneer = 0x14000000 | ((m >> 2) & 0x03ffffff);
15593	dasm_venners_size += 4;
15594
15595	if ((ins >> 16) == DASM_REL_LG
15596	 && *(b-1) < 0) {
15597		/* reuse this veneer for the future jumps to global label */
15598		dasm_labels_veneers[-*(b-1)] = veneer;
15599		/* Dst->globals[*(b-1)] = veneer; */
15600
15601#ifdef HAVE_DISASM
15602	    if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM) {
15603			const char *name = zend_jit_disasm_find_symbol((ptrdiff_t)cp + offset - 4, (int64_t *)(&offset));
15604
15605			if (name && !offset) {
15606				if (strstr(name, "@veneer") == NULL) {
15607					char *new_name;
15608
15609					zend_spprintf(&new_name, 0, "%s@veneer", name);
15610					zend_jit_disasm_add_symbol(new_name, (uint64_t)(uintptr_t)veneer, 4);
15611					efree(new_name);
15612				} else {
15613					zend_jit_disasm_add_symbol(name, (uint64_t)(uintptr_t)veneer, 4);
15614				}
15615			}
15616		}
15617#endif
15618	}
15619
15620	return n;
15621}
15622
15623/*
15624 * Local variables:
15625 * tab-width: 4
15626 * c-basic-offset: 4
15627 * indent-tabs-mode: t
15628 * End:
15629 */
15630