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; 113static size_t tsrm_tls_index = 0; 114static size_t tsrm_tls_offset = 0; 115# ifdef __APPLE__ 116struct TLVDescriptor { 117 void* (*thunk)(struct TLVDescriptor*); 118 uint64_t key; 119 uint64_t offset; 120}; 121typedef struct TLVDescriptor TLVDescriptor; 122# elif defined(__FreeBSD__) 123/* https://github.com/freebsd/freebsd-src/blob/c52ca7dd09066648b1cc40f758289404d68ab886/libexec/rtld-elf/aarch64/reloc.c#L180-L184 */ 124typedef struct TLSDescriptor { 125 void* thunk; 126 int index; 127 size_t offset; 128} TLSDescriptor; 129# endif 130#endif 131 132#define IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1))) 133 134/* Encoding of immediate. */ 135#define MAX_IMM12 0xfff // maximum value for imm12 136#define MAX_IMM16 0xffff // maximum value for imm16 137#define MOVZ_IMM MAX_IMM16 // movz insn 138#define LDR_STR_PIMM64 (MAX_IMM12*8) // ldr/str insn for 64-bit register: pimm is imm12 * 8 139#define LDR_STR_PIMM32 (MAX_IMM12*4) // ldr/str insn for 32-bit register: pimm is imm12 * 4 140#define LDRB_STRB_PIMM MAX_IMM12 // ldrb/strb insn 141 142#define B_IMM (1<<27) // signed imm26 * 4 143#define ADR_IMM (1<<20) // signed imm21 144#define ADRP_IMM (1LL<<32) // signed imm21 * 4096 145 146static bool arm64_may_use_b(const void *addr) 147{ 148 if (addr >= dasm_buf && addr < dasm_end) { 149 return (((char*)dasm_end - (char*)dasm_buf) < B_IMM); 150 } else if (addr >= dasm_end) { 151 return (((char*)addr - (char*)dasm_buf) < B_IMM); 152 } else if (addr < dasm_buf) { 153 return (((char*)dasm_end - (char*)addr) < B_IMM); 154 } 155 return 0; 156} 157 158static bool arm64_may_use_adr(const void *addr) 159{ 160 if (addr >= dasm_buf && addr < dasm_end) { 161 return (((char*)dasm_end - (char*)dasm_buf) < ADR_IMM); 162 } else if (addr >= dasm_end) { 163 return (((char*)addr - (char*)dasm_buf) < ADR_IMM); 164 } else if (addr < dasm_buf) { 165 return (((char*)dasm_end - (char*)addr) < ADR_IMM); 166 } 167 return 0; 168} 169 170static bool arm64_may_use_adrp(const void *addr) 171{ 172 if (addr >= dasm_buf && addr < dasm_end) { 173 return (((char*)dasm_end - (char*)dasm_buf) < ADRP_IMM); 174 } else if (addr >= dasm_end) { 175 return (((char*)addr - (char*)dasm_buf) < ADRP_IMM); 176 } else if (addr < dasm_buf) { 177 return (((char*)dasm_end - (char*)addr) < ADRP_IMM); 178 } 179 return 0; 180} 181 182/* Determine whether "val" falls into two allowed ranges: 183 * Range 1: [0, 0xfff] 184 * Range 2: LSL #12 to Range 1 185 * Used to guard the immediate encoding for add/adds/sub/subs/cmp/cmn instructions. */ 186static bool arm64_may_encode_imm12(const int64_t val) 187{ 188 return (val >= 0 && (val <= MAX_IMM12 || !(val & 0xffffffffff000fff))); 189} 190 191/* Determine whether an immediate value can be encoded as the immediate operand of logical instructions. */ 192static bool logical_immediate_p(uint64_t value, uint32_t reg_size) 193{ 194 /* fast path: power of two */ 195 if (value > 0 && !(value & (value - 1))) { 196 return true; 197 } 198 199 if (reg_size == 32) { 200 if (dasm_imm13((uint32_t)value, (uint32_t)value) != -1) { 201 return true; 202 } 203 } else if (reg_size == 64) { 204 if (dasm_imm13((uint32_t)value, (uint32_t)(value >> 32)) != -1) { 205 return true; 206 } 207 } else { 208 ZEND_UNREACHABLE(); 209 } 210 211 return false; 212} 213 214/* Not Implemented Yet */ 215|.macro NIY 216|| //ZEND_ASSERT(0); 217| brk #0 218|.endmacro 219 220|.macro NIY_STUB 221|| //ZEND_ASSERT(0); 222| brk #0 223|.endmacro 224 225|.macro ADD_HYBRID_SPAD 226||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 227| add sp, sp, # HYBRID_SPAD 228||#endif 229|.endmacro 230 231|.macro SUB_HYBRID_SPAD 232||#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 233| sub sp, sp, # HYBRID_SPAD 234||#endif 235|.endmacro 236 237/* Move address into register. TODO: Support 52-bit address */ 238|.macro LOAD_ADDR, reg, addr 239| // 48-bit virtual address 240|| if (((uintptr_t)(addr)) == 0) { 241| mov reg, xzr 242|| } else if (((uintptr_t)(addr)) <= MOVZ_IMM) { 243| movz reg, #((uint64_t)(addr)) 244|| } else if (arm64_may_use_adr((void*)(addr))) { 245| adr reg, &addr 246|| } else if (arm64_may_use_adrp((void*)(addr))) { 247| adrp reg, &(((uintptr_t)(addr))) 248|| if (((uintptr_t)(addr)) & 0xfff) { 249| add reg, reg, #(((uintptr_t)(addr)) & 0xfff) 250|| } 251|| } else if ((uintptr_t)(addr) & 0xffff) { 252| movz reg, #((uintptr_t)(addr) & 0xffff) 253|| if (((uintptr_t)(addr) >> 16) & 0xffff) { 254| movk reg, #(((uintptr_t)(addr) >> 16) & 0xffff), lsl #16 255|| } 256|| if (((uintptr_t)(addr) >> 32) & 0xffff) { 257| movk reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32 258|| } 259|| } else if (((uintptr_t)(addr) >> 16) & 0xffff) { 260| movz reg, #(((uintptr_t)(addr) >> 16) & 0xffff), lsl #16 261|| if (((uintptr_t)(addr) >> 32) & 0xffff) { 262| movk reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32 263|| } 264|| } else { 265| movz reg, #(((uintptr_t)(addr) >> 32) & 0xffff), lsl #32 266|| } 267|.endmacro 268 269/* Move 32-bit immediate value into register. */ 270|.macro LOAD_32BIT_VAL, reg, val 271|| if (((uint32_t)(val)) <= MOVZ_IMM) { 272| movz reg, #((uint32_t)(val)) 273|| } else if (((uint32_t)(val) & 0xffff)) { 274| movz reg, #((uint32_t)(val) & 0xffff) 275|| if ((((uint32_t)(val) >> 16) & 0xffff)) { 276| movk reg, #(((uint32_t)(val) >> 16) & 0xffff), lsl #16 277|| } 278|| } else { 279| movz reg, #(((uint32_t)(val) >> 16) & 0xffff), lsl #16 280|| } 281|.endmacro 282 283/* Move 64-bit immediate value into register. */ 284|.macro LOAD_64BIT_VAL, reg, val 285|| if (((uint64_t)(val)) == 0) { 286| mov reg, xzr 287|| } else if (((uint64_t)(val)) <= MOVZ_IMM) { 288| movz reg, #((uint64_t)(val)) 289|| } else if (~((uint64_t)(val)) <= MOVZ_IMM) { 290| movn reg, #(~((uint64_t)(val))) 291|| } else if ((uint64_t)(val) & 0xffff) { 292| movz reg, #((uint64_t)(val) & 0xffff) 293|| if (((uint64_t)(val) >> 16) & 0xffff) { 294| movk reg, #(((uint64_t)(val) >> 16) & 0xffff), lsl #16 295|| } 296|| if (((uint64_t)(val) >> 32) & 0xffff) { 297| movk reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32 298|| } 299|| if ((((uint64_t)(val) >> 48) & 0xffff)) { 300| movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48 301|| } 302|| } else if (((uint64_t)(val) >> 16) & 0xffff) { 303| movz reg, #(((uint64_t)(val) >> 16) & 0xffff), lsl #16 304|| if (((uint64_t)(val) >> 32) & 0xffff) { 305| movk reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32 306|| } 307|| if ((((uint64_t)(val) >> 48) & 0xffff)) { 308| movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48 309|| } 310|| } else if (((uint64_t)(val) >> 32) & 0xffff) { 311| movz reg, #(((uint64_t)(val) >> 32) & 0xffff), lsl #32 312|| if ((((uint64_t)(val) >> 48) & 0xffff)) { 313| movk reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48 314|| } 315|| } else { 316| movz reg, #(((uint64_t)(val) >> 48) & 0xffff), lsl #48 317|| } 318|.endmacro 319 320/* Extract the low 8 bits from 'src_reg' into 'dst_reg'. 321 * Note: 0xff can be encoded as imm for 'and' instruction. */ 322|.macro GET_LOW_8BITS, dst_reg, src_reg 323| and dst_reg, src_reg, #0xff 324|.endmacro 325 326/* Bitwise operation with immediate. 'bw_ins' can be and/orr/eor/ands. 327 * 32-bit and 64-bit registers are distinguished. */ 328|.macro BW_OP_32_WITH_CONST, bw_ins, dst_reg, src_reg1, val, tmp_reg 329|| if (val == 0) { 330| bw_ins dst_reg, src_reg1, wzr 331|| } else if (logical_immediate_p((uint32_t)val, 32)) { 332| bw_ins dst_reg, src_reg1, #val 333|| } else { 334| LOAD_32BIT_VAL tmp_reg, val 335| bw_ins dst_reg, src_reg1, tmp_reg 336|| } 337|.endmacro 338 339|.macro BW_OP_64_WITH_CONST, bw_ins, dst_reg, src_reg1, val, tmp_reg 340|| if (val == 0) { 341| bw_ins dst_reg, src_reg1, xzr 342|| } else if (logical_immediate_p(val, 64)) { 343| bw_ins dst_reg, src_reg1, #val 344|| } else { 345| LOAD_64BIT_VAL tmp_reg, val 346| bw_ins dst_reg, src_reg1, tmp_reg 347|| } 348|.endmacro 349 350/* Test bits 'tst' with immediate. 32-bit and 64-bit registers are distinguished. */ 351|.macro TST_32_WITH_CONST, reg, val, tmp_reg 352|| if (val == 0) { 353| tst reg, wzr 354|| } else if (logical_immediate_p((uint32_t)val, 32)) { 355| tst reg, #val 356|| } else { 357| LOAD_32BIT_VAL tmp_reg, val 358| tst reg, tmp_reg 359|| } 360|.endmacro 361 362|.macro TST_64_WITH_CONST, reg, val, tmp_reg 363|| if (val == 0) { 364| tst reg, xzr 365|| } else if (logical_immediate_p(val, 64)) { 366| tst reg, #val 367|| } else { 368| LOAD_64BIT_VAL tmp_reg, val 369| tst reg, tmp_reg 370|| } 371|.endmacro 372 373/* Test bits between 64-bit register with constant 1. */ 374|.macro TST_64_WITH_ONE, reg 375| tst reg, #1 376|.endmacro 377 378/* Compare a register value with immediate. 32-bit and 64-bit registers are distinguished. 379 * Note: Comparing 64-bit register with 32-bit immediate is handled in CMP_64_WITH_CONST_32. */ 380|.macro CMP_32_WITH_CONST, reg, val, tmp_reg 381|| if (val == 0) { 382| cmp reg, wzr 383|| } else if (arm64_may_encode_imm12((int64_t)(val))) { 384| cmp reg, #val 385|| } else if (arm64_may_encode_imm12((int64_t)(-val))) { 386| cmn reg, #-val 387|| } else { 388| LOAD_32BIT_VAL tmp_reg, val 389| cmp reg, tmp_reg 390|| } 391|.endmacro 392 393|.macro CMP_64_WITH_CONST_32, reg, val, tmp_reg 394|| if (val == 0) { 395| cmp reg, xzr 396|| } else if (arm64_may_encode_imm12((int64_t)(val))) { 397| cmp reg, #val 398|| } else if (arm64_may_encode_imm12((int64_t)(-val))) { 399| cmn reg, #-val 400|| } else { 401| LOAD_32BIT_VAL tmp_reg, val 402| cmp reg, tmp_reg 403|| } 404|.endmacro 405 406|.macro CMP_64_WITH_CONST, reg, val, tmp_reg 407|| if (val == 0) { 408| cmp reg, xzr 409|| } else if (arm64_may_encode_imm12((int64_t)(val))) { 410| cmp reg, #val 411|| } else if (arm64_may_encode_imm12((int64_t)(-val))) { 412| cmn reg, #-val 413|| } else { 414| LOAD_64BIT_VAL tmp_reg, val 415| cmp reg, tmp_reg 416|| } 417|.endmacro 418 419/* Add/sub a register value with immediate. 'add_sub_ins' can be add/sub/adds/subs. 420 * 32-bit and 64-bit registers are distinguished. 421 * Note: Case of 64-bit register with 32-bit immediate is handled in ADD_SUB_64_WITH_CONST_32. */ 422|.macro ADD_SUB_32_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg 423|| if (val == 0) { 424| add_sub_ins dst_reg, src_reg1, wzr 425|| } else if (arm64_may_encode_imm12((int64_t)(val))) { 426| add_sub_ins dst_reg, src_reg1, #val 427|| } else { 428| LOAD_32BIT_VAL tmp_reg, val 429| add_sub_ins dst_reg, src_reg1, tmp_reg 430|| } 431|.endmacro 432 433|.macro ADD_SUB_64_WITH_CONST_32, add_sub_ins, dst_reg, src_reg1, val, tmp_reg 434|| if (val == 0) { 435| add_sub_ins dst_reg, src_reg1, xzr 436|| } else if (arm64_may_encode_imm12((int64_t)(val))) { 437| add_sub_ins dst_reg, src_reg1, #val 438|| } else { 439| LOAD_32BIT_VAL tmp_reg, val 440| add_sub_ins dst_reg, src_reg1, tmp_reg 441|| } 442|.endmacro 443 444|.macro ADD_SUB_64_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg 445|| if (val == 0) { 446| add_sub_ins dst_reg, src_reg1, xzr 447|| } else if (arm64_may_encode_imm12((int64_t)(val))) { 448| add_sub_ins dst_reg, src_reg1, #val 449|| } else { 450| LOAD_64BIT_VAL tmp_reg, val 451| add_sub_ins dst_reg, src_reg1, tmp_reg 452|| } 453|.endmacro 454 455/* Memory access(load/store) with 32-bit 'offset'. 456 * Use "unsigned offset" variant if 'offset' can be encoded, otherwise move 'offset' into temp register. 457 * 8-bit, 32-bit and 64-bit registers to be transferred are distinguished. 458 * Note: 'reg' can be used as 'tmp_reg' if 1) 'reg' is one GPR, AND 2) 'reg' != 'base_reg', AND 3) ins is 'ldr'. */ 459|.macro MEM_ACCESS_64_WITH_UOFFSET, ldr_str_ins, reg, base_reg, offset, tmp_reg 460|| if (((uintptr_t)(offset)) > LDR_STR_PIMM64) { 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_32_WITH_UOFFSET, ldr_str_ins, reg, base_reg, offset, tmp_reg 469|| if (((uintptr_t)(offset)) > LDR_STR_PIMM32) { 470| LOAD_32BIT_VAL tmp_reg, offset 471| ldr_str_ins reg, [base_reg, tmp_reg] 472|| } else { 473| ldr_str_ins reg, [base_reg, #(offset)] 474|| } 475|.endmacro 476 477|.macro MEM_ACCESS_8_WITH_UOFFSET, ldrb_strb_ins, reg, base_reg, offset, tmp_reg 478|| if (((uintptr_t)(offset)) > LDRB_STRB_PIMM) { 479| LOAD_32BIT_VAL tmp_reg, offset 480| ldrb_strb_ins reg, [base_reg, tmp_reg] 481|| } else { 482| ldrb_strb_ins reg, [base_reg, #(offset)] 483|| } 484|.endmacro 485 486/* Memory access(load/store) with 64-bit 'offset'. 487 * Use "unsigned offset" variant if 'offset' can be encoded, otherwise move 'offset' into temp register. */ 488|.macro MEM_ACCESS_64_WITH_UOFFSET_64, ldr_str_ins, reg, base_reg, offset, tmp_reg 489|| if (((uintptr_t)(offset)) > LDR_STR_PIMM64) { 490| LOAD_64BIT_VAL tmp_reg, offset 491| ldr_str_ins reg, [base_reg, tmp_reg] 492|| } else { 493| ldr_str_ins reg, [base_reg, #(offset)] 494|| } 495|.endmacro 496 497/* ZTS: get thread local variable "_tsrm_ls_cache" */ 498|.macro LOAD_TSRM_CACHE, reg 499||#ifdef __APPLE__ 500| .long 0xd53bd071 // TODO: hard-coded: mrs TMP3, tpidrro_el0 501| and TMP3, TMP3, #0xfffffffffffffff8 502| MEM_ACCESS_64_WITH_UOFFSET_64 ldr, TMP3, TMP3, (((TLVDescriptor*)tsrm_ls_cache_tcb_offset)->key << 3), TMP1 503| MEM_ACCESS_64_WITH_UOFFSET_64 ldr, reg, TMP3, (((TLVDescriptor*)tsrm_ls_cache_tcb_offset)->offset), TMP1 504||#else 505| .long 0xd53bd051 // TODO: hard-coded: mrs TMP3, tpidr_el0 506|| if (tsrm_ls_cache_tcb_offset == 0) { 507| ldr TMP3, [TMP3, #0] 508| MEM_ACCESS_64_WITH_UOFFSET_64 ldr, TMP3, TMP3, tsrm_tls_index, TMP1 509| MEM_ACCESS_64_WITH_UOFFSET_64 ldr, reg, TMP3, tsrm_tls_offset, TMP1 510|| } else { 511|| ZEND_ASSERT(tsrm_ls_cache_tcb_offset <= LDR_STR_PIMM64); 512| ldr reg, [TMP3, #tsrm_ls_cache_tcb_offset] 513|| } 514||#endif 515|.endmacro 516 517|.macro LOAD_ADDR_ZTS, reg, struct, field 518| .if ZTS 519| LOAD_TSRM_CACHE TMP3 520| ADD_SUB_64_WITH_CONST_32 add, reg, TMP3, (struct.._offset + offsetof(zend_..struct, field)), reg 521| .else 522| LOAD_ADDR reg, &struct.field 523| .endif 524|.endmacro 525 526/* Store address 'addr' into memory 'mem'. */ 527|.macro ADDR_STORE, mem, addr, tmp_reg 528| LOAD_ADDR tmp_reg, addr 529| str tmp_reg, mem 530|.endmacro 531 532/* Store a register value 'reg' into memory 'addr'. 533 * For ZTS mode, base register with unsigned offset variant is used, 534 * and 8-bit/32-bit/64-bit registers to be transferred are distinguished. */ 535|.macro MEM_STORE, str_ins, reg, addr, tmp_reg 536|| if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adr((void*)(addr))) { 537| adr tmp_reg, &addr 538| str_ins reg, [tmp_reg] 539|| } else if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adrp((void*)(addr))) { 540| adrp tmp_reg, &(((uintptr_t)(addr))) 541| str_ins reg, [tmp_reg, #(((uintptr_t)(addr)) & 0xfff)] 542|| } else { 543| LOAD_ADDR tmp_reg, addr 544| str_ins reg, [tmp_reg] 545|| } 546|.endmacro 547 548|.macro MEM_STORE_64_ZTS, str_ins, reg, struct, field, tmp_reg 549| .if ZTS 550| LOAD_TSRM_CACHE TMP3 551| MEM_ACCESS_64_WITH_UOFFSET str_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 552| .else 553| MEM_STORE str_ins, reg, &struct.field, tmp_reg 554| .endif 555|.endmacro 556 557|.macro MEM_STORE_32_ZTS, str_ins, reg, struct, field, tmp_reg 558| .if ZTS 559| LOAD_TSRM_CACHE TMP3 560| MEM_ACCESS_32_WITH_UOFFSET str_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 561| .else 562| MEM_STORE str_ins, reg, &struct.field, tmp_reg 563| .endif 564|.endmacro 565 566|.macro MEM_STORE_8_ZTS, strb_ins, reg, struct, field, tmp_reg 567| .if ZTS 568| LOAD_TSRM_CACHE TMP3 569| MEM_ACCESS_8_WITH_UOFFSET strb_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 570| .else 571| MEM_STORE strb_ins, reg, &struct.field, tmp_reg 572| .endif 573|.endmacro 574 575/* Load value from memory 'addr' and write it into register 'reg'. 576 * For ZTS mode, base register with unsigned offset variant is used, 577 * and 8-bit/32-bit/64-bit registers to be transferred are distinguished. */ 578|.macro MEM_LOAD, ldr_ins, reg, addr, tmp_reg 579|| if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adr((void*)(addr))) { 580| adr tmp_reg, &addr 581| ldr_ins reg, [tmp_reg] 582|| } else if (((uintptr_t)(addr)) > MOVZ_IMM && arm64_may_use_adrp((void*)(addr))) { 583| adrp tmp_reg, &(((uintptr_t)(addr))) 584| ldr_ins reg, [tmp_reg, #(((uintptr_t)(addr)) & 0xfff)] 585|| } else { 586| LOAD_ADDR tmp_reg, addr 587| ldr_ins reg, [tmp_reg] 588|| } 589|.endmacro 590 591|.macro MEM_LOAD_64_ZTS, ldr_ins, reg, struct, field, tmp_reg 592| .if ZTS 593| LOAD_TSRM_CACHE TMP3 594| MEM_ACCESS_64_WITH_UOFFSET ldr_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 595| .else 596| MEM_LOAD ldr_ins, reg, &struct.field, tmp_reg 597| .endif 598|.endmacro 599 600|.macro MEM_LOAD_32_ZTS, ldr_ins, reg, struct, field, tmp_reg 601| .if ZTS 602| LOAD_TSRM_CACHE TMP3 603| MEM_ACCESS_32_WITH_UOFFSET ldr_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 604| .else 605| MEM_LOAD ldr_ins, reg, &struct.field, tmp_reg 606| .endif 607|.endmacro 608 609|.macro MEM_LOAD_8_ZTS, ldrb_ins, reg, struct, field, tmp_reg 610| .if ZTS 611| LOAD_TSRM_CACHE TMP3 612| MEM_ACCESS_8_WITH_UOFFSET ldrb_ins, reg, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 613| .else 614| MEM_LOAD ldrb_ins, reg, &struct.field, tmp_reg 615| .endif 616|.endmacro 617 618/* Conduct arithmetic operation between the value in memory 'addr' and register value in 'reg', 619 * and the computation result is stored back in 'reg'. 'op_ins' can be add/sub. */ 620|.macro MEM_LOAD_OP, op_ins, ldr_ins, reg, addr, tmp_reg1, tmp_reg2 621| MEM_LOAD ldr_ins, tmp_reg1, addr, tmp_reg2 622| op_ins reg, reg, tmp_reg1 623|.endmacro 624 625|.macro MEM_LOAD_OP_ZTS, op_ins, ldr_ins, reg, struct, field, tmp_reg1, tmp_reg2 626| .if ZTS 627| LOAD_TSRM_CACHE TMP3 628| MEM_ACCESS_64_WITH_UOFFSET ldr_ins, tmp_reg2, TMP3, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg1 629| op_ins reg, reg, tmp_reg2 630| .else 631| MEM_LOAD_OP op_ins, ldr_ins, reg, &struct.field, tmp_reg1, tmp_reg2 632| .endif 633|.endmacro 634 635/* Conduct arithmetic operation between the value in memory 'addr' and operand 'op', and the computation 636 * result is stored back to memory 'addr'. Operand 'op' can be either a register value or an immediate value. 637 * Currently, only add instruction is used as 'op_ins'. 638 * Note: It should be guaranteed that the immediate value can be encoded for 'op_ins'. */ 639|.macro MEM_UPDATE, op_ins, ldr_ins, str_ins, op, addr, tmp_reg1, tmp_reg2 640| LOAD_ADDR tmp_reg2, addr 641| ldr_ins, tmp_reg1, [tmp_reg2] 642| op_ins tmp_reg1, tmp_reg1, op 643| str_ins tmp_reg1, [tmp_reg2] 644|.endmacro 645 646|.macro MEM_UPDATE_ZTS, op_ins, ldr_ins, str_ins, op, struct, field, tmp_reg1, tmp_reg2 647| .if ZTS 648| LOAD_TSRM_CACHE TMP3 649|| if (((uintptr_t)(struct.._offset+offsetof(zend_..struct, field))) > LDR_STR_PIMM64) { 650| LOAD_32BIT_VAL tmp_reg1, (struct.._offset+offsetof(zend_..struct, field)) 651| ldr_ins tmp_reg2, [TMP3, tmp_reg1] 652| op_ins tmp_reg2, tmp_reg2, op 653| str_ins tmp_reg2, [TMP3, tmp_reg1] 654|| } else { 655| ldr_ins tmp_reg2, [TMP3, #(struct.._offset+offsetof(zend_..struct, field))] 656| op_ins tmp_reg2, tmp_reg2, op 657| str_ins tmp_reg2, [TMP3, #(struct.._offset+offsetof(zend_..struct, field))] 658|| } 659| .else 660| MEM_UPDATE op_ins, ldr_ins, str_ins, op, &struct.field, tmp_reg1, tmp_reg2 661| .endif 662|.endmacro 663 664|.macro EXT_CALL, func, tmp_reg 665|| if (arm64_may_use_b(func)) { 666| bl &func 667|| } else { 668| LOAD_ADDR tmp_reg, func 669| blr tmp_reg 670|| } 671|.endmacro 672 673|.macro EXT_JMP, func, tmp_reg 674|| if (arm64_may_use_b(func)) { 675| b &func 676|| } else { 677| LOAD_ADDR tmp_reg, func 678| br tmp_reg 679|| } 680|.endmacro 681 682|.macro SAVE_IP 683|| if (GCC_GLOBAL_REGS) { 684| str IP, EX->opline 685|| } 686|.endmacro 687 688|.macro LOAD_IP 689|| if (GCC_GLOBAL_REGS) { 690| ldr IP, EX->opline 691|| } 692|.endmacro 693 694|.macro LOAD_IP_ADDR, addr 695|| if (GCC_GLOBAL_REGS) { 696| LOAD_ADDR IP, addr 697|| } else { 698| ADDR_STORE EX->opline, addr, RX 699|| } 700|.endmacro 701 702|.macro LOAD_IP_ADDR_ZTS, struct, field, tmp_reg 703| .if ZTS 704|| if (GCC_GLOBAL_REGS) { 705| LOAD_TSRM_CACHE IP 706| ADD_SUB_64_WITH_CONST_32 add, IP, IP, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 707|| } else { 708| LOAD_TSRM_CACHE RX 709| ADD_SUB_64_WITH_CONST_32 add, RX, RX, (struct.._offset+offsetof(zend_..struct, field)), tmp_reg 710| str RX, EX->opline 711|| } 712| .else 713| LOAD_IP_ADDR &struct.field 714| .endif 715|.endmacro 716 717|.macro GET_IP, reg 718|| if (GCC_GLOBAL_REGS) { 719| mov reg, IP 720|| } else { 721| ldr reg, EX->opline 722|| } 723|.endmacro 724 725/* Update IP with register 'reg'. Note: shift variant is handled by ADD_IP_SHIFT. */ 726|.macro ADD_IP, reg, tmp_reg 727|| if (GCC_GLOBAL_REGS) { 728| add IP, IP, reg 729|| } else { 730| ldr tmp_reg, EX->opline 731| add tmp_reg, tmp_reg, reg 732| str tmp_reg, EX->opline 733|| } 734|.endmacro 735 736|.macro ADD_IP_SHIFT, reg, shift, tmp_reg 737|| if (GCC_GLOBAL_REGS) { 738| add IP, IP, reg, shift 739|| } else { 740| ldr tmp_reg, EX->opline 741| add tmp_reg, tmp_reg, reg, shift 742| str tmp_reg, EX->opline 743|| } 744|.endmacro 745 746/* Update IP with 32-bit immediate 'val'. */ 747|.macro ADD_IP_WITH_CONST, val, tmp_reg 748|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(val))); 749|| if (GCC_GLOBAL_REGS) { 750| add IP, IP, #val 751|| } else { 752| ldr tmp_reg, EX->opline 753| add tmp_reg, tmp_reg, #val 754| str tmp_reg, EX->opline 755|| } 756|.endmacro 757 758|.macro JMP_IP, tmp_reg 759|| if (GCC_GLOBAL_REGS) { 760| ldr tmp_reg, [IP] 761| br tmp_reg 762|| } else { 763| ldr tmp_reg, EX:CARG1->opline 764| br tmp_reg 765|| } 766|.endmacro 767 768|.macro CMP_IP, addr, tmp_reg1, tmp_reg2 769| LOAD_ADDR tmp_reg1, addr 770|| if (GCC_GLOBAL_REGS) { 771| cmp IP, tmp_reg1 772|| } else { 773| ldr tmp_reg2, EX->opline 774| cmp tmp_reg2, tmp_reg1 775|| } 776|.endmacro 777 778|.macro LOAD_ZVAL_ADDR, reg, addr 779|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 780| LOAD_ADDR reg, Z_ZV(addr) 781|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 782|| if (Z_OFFSET(addr)) { 783| ADD_SUB_64_WITH_CONST_32 add, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), reg 784|| } else { 785|| if (Z_REG(addr) == ZREG_RSP) { 786| mov reg, sp 787|| } else { 788| mov reg, Rx(Z_REG(addr)) 789|| } 790|| } 791|| } else { 792|| ZEND_UNREACHABLE(); 793|| } 794|.endmacro 795 796|.macro GET_Z_TYPE_INFO, reg, zv 797| ldr reg, [zv, #offsetof(zval,u1.type_info)] 798|.endmacro 799 800|.macro SET_Z_TYPE_INFO, zv, type, tmp_reg 801| LOAD_32BIT_VAL tmp_reg, type 802| str tmp_reg, [zv, #offsetof(zval,u1.type_info)] 803|.endmacro 804 805|.macro GET_ZVAL_TYPE, reg, addr, tmp_reg 806|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 807| MEM_ACCESS_8_WITH_UOFFSET ldrb, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.v.type), tmp_reg 808|.endmacro 809 810|.macro GET_ZVAL_TYPE_INFO, reg, addr, tmp_reg 811|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 812| MEM_ACCESS_32_WITH_UOFFSET ldr, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg 813|.endmacro 814 815|.macro SET_ZVAL_TYPE_INFO, addr, type, tmp_reg1, tmp_reg2 816|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 817| LOAD_32BIT_VAL tmp_reg1, type 818| MEM_ACCESS_32_WITH_UOFFSET str, tmp_reg1, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg2 819|.endmacro 820 821|.macro SET_ZVAL_TYPE_INFO_FROM_REG, addr, reg, tmp_reg 822|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 823| MEM_ACCESS_32_WITH_UOFFSET str, reg, Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval,u1.type_info), tmp_reg 824|.endmacro 825 826|.macro GET_Z_PTR, reg, zv 827| ldr reg, [zv] 828|.endmacro 829 830|.macro GET_ZVAL_PTR, reg, addr, tmp_reg 831|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 832| MEM_ACCESS_64_WITH_UOFFSET ldr, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg 833|.endmacro 834 835|.macro SET_ZVAL_PTR, addr, reg, tmp_reg 836|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 837| MEM_ACCESS_64_WITH_UOFFSET str, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg 838|.endmacro 839 840|.macro UNDEF_OPLINE_RESULT, tmp_reg 841| ldr REG0, EX->opline 842| ldr REG0w, OP:REG0->result.var 843| add REG0, FP, REG0 844| SET_Z_TYPE_INFO REG0, IS_UNDEF, tmp_reg 845|.endmacro 846 847|.macro UNDEF_OPLINE_RESULT_IF_USED, tmp_reg1, tmp_reg2 848| ldrb tmp_reg1, OP:RX->result_type 849| TST_32_WITH_CONST tmp_reg1, (IS_TMP_VAR|IS_VAR), tmp_reg2 850| beq >1 851| ldr REG0w, OP:RX->result.var 852| add REG0, FP, REG0 853| SET_Z_TYPE_INFO REG0, IS_UNDEF, tmp_reg1 854|1: 855|.endmacro 856 857/* Floating-point comparison between register 'reg' and value from memory 'addr'. 858 * Note: the equivalent macros in JIT/x86 are SSE_AVX_OP and SSE_OP. */ 859|.macro DOUBLE_CMP, reg, addr, tmp_reg, fp_tmp_reg 860|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 861| MEM_LOAD ldr, Rd(fp_tmp_reg-ZREG_V0), Z_ZV(addr), Rx(tmp_reg) 862| fcmp Rd(reg-ZREG_V0), Rd(fp_tmp_reg-ZREG_V0) 863|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 864| MEM_ACCESS_64_WITH_UOFFSET ldr, Rd(fp_tmp_reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg) 865| fcmp Rd(reg-ZREG_V0), Rd(fp_tmp_reg-ZREG_V0) 866|| } else if (Z_MODE(addr) == IS_REG) { 867| fcmp Rd(reg-ZREG_V0), Rd(Z_REG(addr)-ZREG_V0) 868|| } else { 869|| ZEND_UNREACHABLE(); 870|| } 871|.endmacro 872 873/* Convert LONG value 'val' into DOUBLE type, and move it into FP register 'reg'. 874 * Note: the equivalent macro in JIT/x86 is SSE_GET_LONG. */ 875|.macro DOUBLE_GET_LONG, reg, val, tmp_reg 876|| if (val == 0) { 877| fmov Rd(reg-ZREG_V0), xzr // TODO: "movi d0, #0" is not recognized by DynASM/arm64 878|| } else { 879| LOAD_64BIT_VAL Rx(tmp_reg), val 880| scvtf Rd(reg-ZREG_V0), Rx(tmp_reg) 881|| } 882|.endmacro 883 884/* Convert LONG value from memory 'addr' into DOUBLE type, and move it into FP register 'reg'. 885 * Note: the equivalent macro in JIT/x86 is SSE_GET_ZVAL_LVAL. */ 886|.macro DOUBLE_GET_ZVAL_LVAL, reg, addr, tmp_reg1, tmp_reg2 887|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 888| DOUBLE_GET_LONG reg, Z_LVAL_P(Z_ZV(addr)), tmp_reg1 889|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 890| MEM_ACCESS_64_WITH_UOFFSET ldr, Rx(tmp_reg1), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg2) 891| scvtf Rd(reg-ZREG_V0), Rx(tmp_reg1) 892|| } else if (Z_MODE(addr) == IS_REG) { 893| scvtf Rd(reg-ZREG_V0), Rx(Z_REG(addr)) 894|| } else { 895|| ZEND_UNREACHABLE(); 896|| } 897|.endmacro 898 899/* Floating-point arithmetic operation between two FP registers. 900 * Note: the equivalent macro in JIT/x86 is AVX_MATH_REG. */ 901|.macro DOUBLE_MATH_REG, opcode, dst_reg, op1_reg, op2_reg 902|| switch (opcode) { 903|| case ZEND_ADD: 904| fadd Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0) 905|| break; 906|| case ZEND_SUB: 907| fsub Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0) 908|| break; 909|| case ZEND_MUL: 910| fmul Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0) 911|| break; 912|| case ZEND_DIV: 913| fdiv Rd(dst_reg-ZREG_V0), Rd(op1_reg-ZREG_V0), Rd(op2_reg-ZREG_V0) 914|| break; 915|| } 916|.endmacro 917 918/* Conduct binary operation between register 'reg' and value from memory 'addr', 919 * and the computation result is stored in 'reg'. 920 * For LONG_ADD_SUB, 'add_sub_ins' can be adds/subs. For LONG_BW_OP, 'bw_ins' can be and/orr/eor. 921 * For LONG_CMP, 'cmp' instruction is used by default and only flag registers are affected. 922 * Note: the equivalent macro in JIT/x86 is LONG_OP. */ 923|.macro LONG_ADD_SUB, add_sub_ins, reg, addr, tmp_reg 924|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 925| ADD_SUB_64_WITH_CONST add_sub_ins, Rx(reg), Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg 926|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 927| MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg 928| add_sub_ins Rx(reg), Rx(reg), tmp_reg 929|| } else if (Z_MODE(addr) == IS_REG) { 930| add_sub_ins Rx(reg), Rx(reg), Rx(Z_REG(addr)) 931|| } else { 932|| ZEND_UNREACHABLE(); 933|| } 934|.endmacro 935 936|.macro LONG_BW_OP, bw_ins, reg, addr, tmp_reg 937|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 938| BW_OP_64_WITH_CONST bw_ins, Rx(reg), Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg 939|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 940| MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg 941| bw_ins Rx(reg), Rx(reg), tmp_reg 942|| } else if (Z_MODE(addr) == IS_REG) { 943| bw_ins Rx(reg), Rx(reg), Rx(Z_REG(addr)) 944|| } else { 945|| ZEND_UNREACHABLE(); 946|| } 947|.endmacro 948 949|.macro LONG_CMP, reg, addr, tmp_reg 950|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 951| CMP_64_WITH_CONST Rx(reg), Z_LVAL_P(Z_ZV(addr)), tmp_reg 952|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 953| MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg 954| cmp Rx(reg), tmp_reg 955|| } else if (Z_MODE(addr) == IS_REG) { 956| cmp Rx(reg), Rx(Z_REG(addr)) 957|| } else { 958|| ZEND_UNREACHABLE(); 959|| } 960|.endmacro 961 962/* Conduct add/sub between value from memory 'addr' and an immediate value 'val', and 963 * the computation result is stored back into 'addr'. 964 * Note: it should be guaranteed that 'val' can be encoded into add/sub instruction. */ 965|.macro LONG_ADD_SUB_WITH_IMM, add_sub_ins, addr, val, tmp_reg1, tmp_reg2 966|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(val))); 967|| if (Z_MODE(addr) == IS_MEM_ZVAL) { 968|| if (((uint32_t)(Z_OFFSET(addr))) > LDR_STR_PIMM64) { 969| LOAD_32BIT_VAL tmp_reg2, Z_OFFSET(addr) 970| ldr tmp_reg1, [Rx(Z_REG(addr)), tmp_reg2] 971| add_sub_ins tmp_reg1, tmp_reg1, #val 972| str tmp_reg1, [Rx(Z_REG(addr)), tmp_reg2] 973|| } else { 974| ldr tmp_reg1, [Rx(Z_REG(addr)), #Z_OFFSET(addr)] 975| add_sub_ins tmp_reg1, tmp_reg1, #val 976| str tmp_reg1, [Rx(Z_REG(addr)), #Z_OFFSET(addr)] 977|| } 978|| } else if (Z_MODE(addr) == IS_REG) { 979| add_sub_ins Rx(Z_REG(addr)), Rx(Z_REG(addr)), #val 980|| } else { 981|| ZEND_UNREACHABLE(); 982|| } 983|.endmacro 984 985/* Compare value from memory 'addr' with immediate value 'val'. 986 * Note: the equivalent macro in JIT/x86 is LONG_OP_WITH_CONST. */ 987|.macro LONG_CMP_WITH_CONST, addr, val, tmp_reg1, tmp_reg2 988|| if (Z_MODE(addr) == IS_MEM_ZVAL) { 989| MEM_ACCESS_64_WITH_UOFFSET ldr, tmp_reg1, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg2 990| CMP_64_WITH_CONST tmp_reg1, val, tmp_reg2 991|| } else if (Z_MODE(addr) == IS_REG) { 992| CMP_64_WITH_CONST Rx(Z_REG(addr)), val, tmp_reg1 993|| } else { 994|| ZEND_UNREACHABLE(); 995|| } 996|.endmacro 997 998|.macro GET_ZVAL_LVAL, reg, addr, tmp_reg 999|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 1000|| if (Z_LVAL_P(Z_ZV(addr)) == 0) { 1001| mov Rx(reg), xzr 1002|| } else { 1003| LOAD_64BIT_VAL Rx(reg), Z_LVAL_P(Z_ZV(addr)) 1004|| } 1005|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 1006| MEM_ACCESS_64_WITH_UOFFSET ldr, Rx(reg), Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg 1007|| } else if (Z_MODE(addr) == IS_REG) { 1008|| if (reg != Z_REG(addr)) { 1009| mov Rx(reg), Rx(Z_REG(addr)) 1010|| } 1011|| } else { 1012|| ZEND_UNREACHABLE(); 1013|| } 1014|.endmacro 1015 1016|.macro LONG_MATH, opcode, reg, addr, tmp_reg1 1017|| switch (opcode) { 1018|| case ZEND_ADD: 1019| LONG_ADD_SUB adds, reg, addr, tmp_reg1 1020|| break; 1021|| case ZEND_SUB: 1022| LONG_ADD_SUB subs, reg, addr, tmp_reg1 1023|| break; 1024|| case ZEND_BW_OR: 1025| LONG_BW_OP orr, reg, addr, tmp_reg1 1026|| break; 1027|| case ZEND_BW_AND: 1028| LONG_BW_OP and, reg, addr, tmp_reg1 1029|| break; 1030|| case ZEND_BW_XOR: 1031| LONG_BW_OP eor, reg, addr, tmp_reg1 1032|| break; 1033|| default: 1034|| ZEND_UNREACHABLE(); 1035|| } 1036|.endmacro 1037 1038|.macro LONG_MATH_REG, opcode, dst_reg, src_reg1, src_reg2 1039|| switch (opcode) { 1040|| case ZEND_ADD: 1041| adds dst_reg, src_reg1, src_reg2 1042|| break; 1043|| case ZEND_SUB: 1044| subs dst_reg, src_reg1, src_reg2 1045|| break; 1046|| case ZEND_BW_OR: 1047| orr dst_reg, src_reg1, src_reg2 1048|| break; 1049|| case ZEND_BW_AND: 1050| and dst_reg, src_reg1, src_reg2 1051|| break; 1052|| case ZEND_BW_XOR: 1053| eor dst_reg, src_reg1, src_reg2 1054|| break; 1055|| default: 1056|| ZEND_UNREACHABLE(); 1057|| } 1058|.endmacro 1059 1060/* Store LONG value into memory 'addr'. 1061 * This LONG value can be an immediate value i.e. 'val' in macro SET_ZVAL_LVAL, or 1062 * a register value i.e. 'reg' in macro SET_ZVAL_LVAL_FROM_REG. */ 1063|.macro SET_ZVAL_LVAL_FROM_REG, addr, reg, tmp_reg 1064|| if (Z_MODE(addr) == IS_REG) { 1065| mov Rx(Z_REG(addr)), reg 1066|| } else { 1067|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 1068| MEM_ACCESS_64_WITH_UOFFSET str, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), tmp_reg 1069|| } 1070|.endmacro 1071 1072|.macro SET_ZVAL_LVAL, addr, val, tmp_reg1, tmp_reg2 1073|| if (val == 0) { 1074| SET_ZVAL_LVAL_FROM_REG addr, xzr, tmp_reg2 1075|| } else { 1076| LOAD_64BIT_VAL tmp_reg1, val 1077| SET_ZVAL_LVAL_FROM_REG addr, tmp_reg1, tmp_reg2 1078|| } 1079|.endmacro 1080 1081/* Store DOUBLE value from FP register 'reg' into memory 'addr'. 1082 * Note: the equivalent macro in JIT/x86 is SSE_SET_ZVAL_DVAL. */ 1083|.macro SET_ZVAL_DVAL, addr, reg, tmp_reg 1084|| if (Z_MODE(addr) == IS_REG) { 1085|| if (reg != Z_REG(addr)) { 1086| fmov Rd(Z_REG(addr)-ZREG_V0), Rd(reg-ZREG_V0) 1087|| } 1088|| } else { 1089|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 1090| MEM_ACCESS_64_WITH_UOFFSET str, Rd(reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg) 1091|| } 1092|.endmacro 1093 1094/* Load DOUBLE value from memory 'addr' into FP register 'reg'. 1095 * Note: the equivalent macro in JIT/x86 is SSE_GET_ZVAL_DVAL. */ 1096|.macro GET_ZVAL_DVAL, reg, addr, tmp_reg 1097|| if (Z_MODE(addr) != IS_REG || reg != Z_REG(addr)) { 1098|| if (Z_MODE(addr) == IS_CONST_ZVAL) { 1099| MEM_LOAD ldr, Rd(reg-ZREG_V0), Z_ZV(addr), Rx(tmp_reg) 1100|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) { 1101| MEM_ACCESS_64_WITH_UOFFSET ldr, Rd(reg-ZREG_V0), Rx(Z_REG(addr)), Z_OFFSET(addr), Rx(tmp_reg) 1102|| } else if (Z_MODE(addr) == IS_REG) { 1103| fmov Rd(reg-ZREG_V0), Rd(Z_REG(addr)-ZREG_V0) 1104|| } else { 1105|| ZEND_UNREACHABLE(); 1106|| } 1107|| } 1108|.endmacro 1109 1110|.macro ZVAL_COPY_CONST, dst_addr, dst_info, dst_def_info, zv, tmp_reg1, tmp_reg2, fp_tmp_reg 1111|| if (Z_TYPE_P(zv) > IS_TRUE) { 1112|| if (Z_TYPE_P(zv) == IS_DOUBLE) { 1113|| zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : fp_tmp_reg; 1114| MEM_LOAD ldr, Rd(dst_reg-ZREG_V0), zv, Rx(tmp_reg1) 1115| SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2 1116|| } else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) { 1117|| zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? Z_REG(dst_addr) : fp_tmp_reg; 1118| DOUBLE_GET_LONG dst_reg, Z_LVAL_P(zv), tmp_reg1 1119| SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2 1120|| } else { 1121| // In x64, if the range of this LONG value can be represented via INT type, only move the low 32 bits into dst_addr. 1122| // Note that imm32 is signed extended to 64 bits during mov. 1123| // 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 1124| // needed for 32-bit value, an extra ext insn is needed for 32-bit value. 1125| SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2) 1126|| } 1127|| } 1128|| if (Z_MODE(dst_addr) == IS_MEM_ZVAL) { 1129|| if (dst_def_info == MAY_BE_DOUBLE) { 1130|| if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) { 1131| SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2) 1132|| } 1133|| } 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) { 1134| SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2) 1135|| } 1136|| } 1137|.endmacro 1138 1139|.macro ZVAL_COPY_CONST_2, dst_addr, res_addr, dst_info, dst_def_info, zv, tmp_reg1, tmp_reg2, fp_tmp_reg 1140|| if (Z_TYPE_P(zv) > IS_TRUE) { 1141|| if (Z_TYPE_P(zv) == IS_DOUBLE) { 1142|| zend_reg dst_reg = (Z_MODE(dst_addr) == IS_REG) ? 1143|| Z_REG(dst_addr) : ((Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : fp_tmp_reg); 1144| MEM_LOAD ldr, Rd(dst_reg-ZREG_V0), zv, Rx(tmp_reg1) 1145| SET_ZVAL_DVAL dst_addr, dst_reg, tmp_reg2 1146| SET_ZVAL_DVAL res_addr, dst_reg, tmp_reg2 1147|| } else if (Z_TYPE_P(zv) == IS_LONG && dst_def_info == MAY_BE_DOUBLE) { 1148|| if (Z_MODE(dst_addr) == IS_REG) { 1149| DOUBLE_GET_LONG Z_REG(dst_addr), Z_LVAL_P(zv), tmp_reg1 1150| SET_ZVAL_DVAL res_addr, Z_REG(dst_addr), tmp_reg2 1151|| } else if (Z_MODE(res_addr) == IS_REG) { 1152| DOUBLE_GET_LONG Z_REG(res_addr), Z_LVAL_P(zv), tmp_reg1 1153| SET_ZVAL_DVAL dst_addr, Z_REG(res_addr), tmp_reg2 1154|| } else { 1155| DOUBLE_GET_LONG fp_tmp_reg, Z_LVAL_P(zv), tmp_reg1 1156| SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg2 1157| SET_ZVAL_DVAL res_addr, fp_tmp_reg, tmp_reg2 1158|| } 1159|| } else { 1160|| if (Z_MODE(dst_addr) == IS_REG) { 1161| SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2) 1162| SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(dst_addr)), Rx(tmp_reg1) 1163|| } else if (Z_MODE(res_addr) == IS_REG) { 1164| SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2) 1165| SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(res_addr)), Rx(tmp_reg1) 1166|| } else { 1167| SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2) 1168| SET_ZVAL_LVAL res_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2) 1169|| } 1170|| } 1171|| } 1172|| if (Z_MODE(dst_addr) == IS_MEM_ZVAL) { 1173|| if (dst_def_info == MAY_BE_DOUBLE) { 1174|| if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) { 1175| SET_ZVAL_TYPE_INFO dst_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2) 1176|| } 1177|| } 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) { 1178| SET_ZVAL_TYPE_INFO dst_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2) 1179|| } 1180|| } 1181|| if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 1182|| if (dst_def_info == MAY_BE_DOUBLE) { 1183| SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, Rw(tmp_reg1), Rx(tmp_reg2) 1184|| } else { 1185| SET_ZVAL_TYPE_INFO res_addr, Z_TYPE_INFO_P(zv), Rw(tmp_reg1), Rx(tmp_reg2) 1186|| } 1187|| } 1188|.endmacro 1189 1190// the same as above, but "src" may overlap with "reg1" 1191// Useful info would be stored into reg1 and reg2, and they might be used afterward. 1192|.macro ZVAL_COPY_VALUE, dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg1, tmp_reg2, fp_tmp_reg 1193| ZVAL_COPY_VALUE_V dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg1, fp_tmp_reg 1194|| if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) && 1195|| !(src_info & MAY_BE_GUARD) && 1196|| has_concrete_type(src_info & MAY_BE_ANY)) { 1197|| if (Z_MODE(dst_addr) == IS_MEM_ZVAL) { 1198|| if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD))) { 1199|| uint32_t type = concrete_type(src_info); 1200| SET_ZVAL_TYPE_INFO dst_addr, type, Rw(tmp_reg1), Rx(tmp_reg2) 1201|| } 1202|| } 1203|| } else { 1204| GET_ZVAL_TYPE_INFO Rw(reg1), src_addr, Rx(tmp_reg1) 1205| SET_ZVAL_TYPE_INFO_FROM_REG dst_addr, Rw(reg1), Rx(tmp_reg1) 1206|| } 1207|.endmacro 1208 1209|.macro ZVAL_COPY_VALUE_V, dst_addr, dst_info, src_addr, src_info, reg1, reg2, tmp_reg, fp_tmp_reg 1210|| if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) { 1211|| if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_LONG) { 1212|| if (Z_MODE(src_addr) == IS_REG) { 1213|| if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) { 1214| SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg) 1215|| } 1216|| } else if (Z_MODE(dst_addr) == IS_REG) { 1217| GET_ZVAL_LVAL Z_REG(dst_addr), src_addr, Rx(tmp_reg) 1218|| } else { 1219| GET_ZVAL_LVAL reg2, src_addr, Rx(tmp_reg) 1220| SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(reg2), Rx(tmp_reg) 1221|| } 1222|| } else if ((src_info & (MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) { 1223|| if (Z_MODE(src_addr) == IS_REG) { 1224| SET_ZVAL_DVAL dst_addr, Z_REG(src_addr), tmp_reg 1225|| } else if (Z_MODE(dst_addr) == IS_REG) { 1226| GET_ZVAL_DVAL Z_REG(dst_addr), src_addr, tmp_reg 1227|| } else { 1228| GET_ZVAL_DVAL fp_tmp_reg, src_addr, tmp_reg 1229| SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg 1230|| } 1231|| // Combine the following two branches. 1232|| // } else if (!(src_info & (MAY_BE_DOUBLE|MAY_BE_GUARD))) { 1233|| } else { 1234| GET_ZVAL_PTR Rx(reg2), src_addr, Rx(tmp_reg) 1235| SET_ZVAL_PTR dst_addr, Rx(reg2), Rx(tmp_reg) 1236|| } 1237|| } 1238|.endmacro 1239 1240|.macro ZVAL_COPY_VALUE_2, dst_addr, dst_info, res_addr, src_addr, src_info, reg1, reg2, tmp_reg, tmp_reg2, fp_tmp_reg 1241|| if (src_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) { 1242|| if ((src_info & MAY_BE_ANY) == MAY_BE_LONG) { 1243|| if (Z_MODE(src_addr) == IS_REG) { 1244|| if (Z_MODE(dst_addr) != IS_REG || Z_REG(dst_addr) != Z_REG(src_addr)) { 1245| SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg) 1246|| } 1247|| if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(src_addr)) { 1248| SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(src_addr)), Rx(tmp_reg) 1249|| } 1250|| } else if (Z_MODE(dst_addr) == IS_REG) { 1251| GET_ZVAL_LVAL Z_REG(dst_addr), src_addr, Rx(tmp_reg) 1252|| if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != Z_REG(dst_addr)) { 1253| SET_ZVAL_LVAL_FROM_REG res_addr, Rx(Z_REG(dst_addr)), Rx(tmp_reg) 1254|| } 1255|| } else if (Z_MODE(res_addr) == IS_REG) { 1256| GET_ZVAL_LVAL Z_REG(res_addr), src_addr, Rx(tmp_reg) 1257| SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(Z_REG(res_addr)), Rx(tmp_reg) 1258|| } else { 1259| GET_ZVAL_LVAL reg2, src_addr, Rx(tmp_reg) 1260| SET_ZVAL_LVAL_FROM_REG dst_addr, Rx(reg2), Rx(tmp_reg) 1261| SET_ZVAL_LVAL_FROM_REG res_addr, Rx(reg2), Rx(tmp_reg) 1262|| } 1263|| } else if ((src_info & MAY_BE_ANY) == MAY_BE_DOUBLE) { 1264|| if (Z_MODE(src_addr) == IS_REG) { 1265| SET_ZVAL_DVAL dst_addr, Z_REG(src_addr), tmp_reg 1266| SET_ZVAL_DVAL res_addr, Z_REG(src_addr), tmp_reg 1267|| } else if (Z_MODE(dst_addr) == IS_REG) { 1268| GET_ZVAL_DVAL Z_REG(dst_addr), src_addr, tmp_reg 1269| SET_ZVAL_DVAL res_addr, Z_REG(dst_addr), tmp_reg 1270|| } else if (Z_MODE(res_addr) == IS_REG) { 1271| GET_ZVAL_DVAL Z_REG(res_addr), src_addr, tmp_reg 1272| SET_ZVAL_DVAL dst_addr, Z_REG(res_addr), tmp_reg 1273|| } else { 1274| GET_ZVAL_DVAL fp_tmp_reg, src_addr, tmp_reg 1275| SET_ZVAL_DVAL dst_addr, fp_tmp_reg, tmp_reg 1276| SET_ZVAL_DVAL res_addr, fp_tmp_reg, tmp_reg 1277|| } 1278|| } else { 1279| GET_ZVAL_PTR Rx(reg2), src_addr, Rx(tmp_reg) 1280| SET_ZVAL_PTR dst_addr, Rx(reg2), Rx(tmp_reg) 1281| SET_ZVAL_PTR res_addr, Rx(reg2), Rx(tmp_reg) 1282|| } 1283|| } 1284|| if ((src_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)) && 1285|| has_concrete_type(src_info & MAY_BE_ANY)) { 1286|| uint32_t type = concrete_type(src_info); 1287|| if (Z_MODE(dst_addr) == IS_MEM_ZVAL) { 1288|| if ((dst_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) != (src_info & (MAY_BE_ANY|MAY_BE_UNDEF))) { 1289| SET_ZVAL_TYPE_INFO dst_addr, type, Rw(tmp_reg), Rx(tmp_reg2) 1290|| } 1291|| } 1292|| if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 1293| SET_ZVAL_TYPE_INFO res_addr, type, Rw(tmp_reg), Rx(tmp_reg2) 1294|| } 1295|| } else { 1296| GET_ZVAL_TYPE_INFO Rw(reg1), src_addr, Rx(tmp_reg) 1297| SET_ZVAL_TYPE_INFO_FROM_REG dst_addr, Rw(reg1), Rx(tmp_reg) 1298| SET_ZVAL_TYPE_INFO_FROM_REG res_addr, Rw(reg1), Rx(tmp_reg) 1299|| } 1300|.endmacro 1301 1302|.macro IF_UNDEF, type_reg, label 1303| cbz type_reg, label 1304|.endmacro 1305 1306|.macro IF_TYPE, type, val, label 1307|| if (val == 0) { 1308| cbz type, label 1309|| } else { 1310| cmp type, #val 1311| beq label 1312|| } 1313|.endmacro 1314 1315|.macro IF_NOT_TYPE, type, val, label 1316|| if (val == 0) { 1317| cbnz type, label 1318|| } else { 1319| cmp type, #val 1320| bne label 1321|| } 1322|.endmacro 1323 1324|.macro IF_Z_TYPE, zv, val, label, tmp_reg 1325| ldrb tmp_reg, [zv, #offsetof(zval, u1.v.type)] 1326| IF_TYPE tmp_reg, val, label 1327|.endmacro 1328 1329|.macro IF_NOT_Z_TYPE, zv, val, label, tmp_reg 1330| ldrb tmp_reg, [zv, #offsetof(zval, u1.v.type)] 1331| IF_NOT_TYPE tmp_reg, val, label 1332|.endmacro 1333 1334|.macro CMP_ZVAL_TYPE, addr, val, tmp_reg 1335|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 1336| MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg) 1337| cmp Rw(tmp_reg), #val 1338|.endmacro 1339 1340|.macro IF_ZVAL_TYPE, addr, val, label, tmp_reg 1341|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 1342| MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg) 1343| IF_TYPE Rw(tmp_reg), val, label 1344|.endmacro 1345 1346|.macro IF_NOT_ZVAL_TYPE, addr, val, label, tmp_reg 1347|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 1348| MEM_ACCESS_8_WITH_UOFFSET ldrb, Rw(tmp_reg), Rx(Z_REG(addr)), Z_OFFSET(addr)+offsetof(zval, u1.v.type), Rx(tmp_reg) 1349| IF_NOT_TYPE Rw(tmp_reg), val, label 1350|.endmacro 1351 1352|.macro IF_FLAGS, type_flags, mask, label, tmp_reg 1353| TST_32_WITH_CONST type_flags, mask, tmp_reg 1354| bne label 1355|.endmacro 1356 1357|.macro IF_NOT_FLAGS, type_flags, mask, label, tmp_reg 1358| TST_32_WITH_CONST type_flags, mask, tmp_reg 1359| beq label 1360|.endmacro 1361 1362|.macro IF_REFCOUNTED, type_flags, label, tmp_reg 1363| TST_32_WITH_CONST type_flags, (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), tmp_reg 1364| bne label 1365|.endmacro 1366 1367|.macro IF_NOT_REFCOUNTED, type_flags, label, tmp_reg 1368| TST_32_WITH_CONST type_flags, (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT), tmp_reg 1369| beq label 1370|.endmacro 1371 1372|.macro IF_ZVAL_FLAGS, addr, mask, label, tmp_reg1, tmp_reg2 1373|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 1374| 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) 1375| IF_FLAGS Rw(tmp_reg1), mask, label, Rw(tmp_reg2) 1376|.endmacro 1377 1378|.macro IF_NOT_ZVAL_FLAGS, addr, mask, label, tmp_reg1, tmp_reg2 1379|| ZEND_ASSERT(Z_MODE(addr) == IS_MEM_ZVAL); 1380| 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) 1381| IF_NOT_FLAGS Rw(tmp_reg1), mask, label, Rw(tmp_reg2) 1382|.endmacro 1383 1384|.macro IF_ZVAL_REFCOUNTED, addr, label, tmp_reg1, tmp_reg2 1385| IF_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label, tmp_reg1, tmp_reg2 1386|.endmacro 1387 1388|.macro IF_NOT_ZVAL_REFCOUNTED, addr, label, tmp_reg1, tmp_reg2 1389| IF_NOT_ZVAL_FLAGS addr, IS_TYPE_REFCOUNTED, label, tmp_reg1, tmp_reg2 1390|.endmacro 1391 1392|.macro IF_NOT_ZVAL_COLLECTABLE, addr, label, tmp_reg1, tmp_reg2 1393| IF_NOT_ZVAL_FLAGS addr, IS_TYPE_COLLECTABLE, label, tmp_reg1, tmp_reg2 1394|.endmacro 1395 1396|.macro GC_ADDREF, zv, tmp_reg 1397| ldr tmp_reg, [zv] 1398| add tmp_reg, tmp_reg, #1 1399| str tmp_reg, [zv] 1400|.endmacro 1401 1402|.macro GC_ADDREF_2, zv, tmp_reg 1403| ldr tmp_reg, [zv] 1404| add tmp_reg, tmp_reg, #2 1405| str tmp_reg, [zv] 1406|.endmacro 1407 1408|.macro GC_DELREF, zv, tmp_reg 1409| ldr tmp_reg, [zv] 1410| subs tmp_reg, tmp_reg, #1 1411| str tmp_reg, [zv] 1412|.endmacro 1413 1414|.macro IF_GC_MAY_NOT_LEAK, ptr, label, tmp_reg1, tmp_reg2 1415| ldr tmp_reg1, [ptr, #4] 1416| TST_32_WITH_CONST tmp_reg1, (GC_INFO_MASK | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)), tmp_reg2 1417| bne label 1418|.endmacro 1419 1420|.macro ADDREF_CONST, zv, tmp_reg1, tmp_reg2 1421| LOAD_ADDR tmp_reg1, Z_PTR_P(zv) 1422| ldr tmp_reg2, [tmp_reg1] 1423| add tmp_reg2, tmp_reg2, #1 1424| str tmp_reg2, [tmp_reg1] 1425|.endmacro 1426 1427|.macro ADDREF_CONST_2, zv, tmp_reg1, tmp_reg2 1428| LOAD_ADDR tmp_reg1, Z_PTR_P(zv) 1429| ldr tmp_reg2, [tmp_reg1] 1430| add tmp_reg2, tmp_reg2, #2 1431| str tmp_reg2, [tmp_reg1] 1432|.endmacro 1433 1434|.macro TRY_ADDREF, val_info, type_flags_reg, value_ptr_reg, tmp_reg 1435|| if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 1436|| if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 1437| IF_NOT_REFCOUNTED type_flags_reg, >1, tmp_reg 1438|| } 1439| GC_ADDREF value_ptr_reg, tmp_reg 1440|1: 1441|| } 1442|.endmacro 1443 1444|.macro TRY_ADDREF_2, val_info, type_flags_reg, value_ptr_reg, tmp_reg 1445|| if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 1446|| if (val_info & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 1447| IF_NOT_REFCOUNTED type_flags_reg, >1, tmp_reg 1448|| } 1449| ldr tmp_reg, [value_ptr_reg] 1450| add tmp_reg, tmp_reg, #2 1451| str tmp_reg, [value_ptr_reg] 1452|1: 1453|| } 1454|.endmacro 1455 1456|.macro ZVAL_DEREF, reg, info, tmp_reg 1457|| if (info & MAY_BE_REF) { 1458| IF_NOT_Z_TYPE, reg, IS_REFERENCE, >1, tmp_reg 1459| GET_Z_PTR reg, reg 1460| add reg, reg, #offsetof(zend_reference, val) 1461|1: 1462|| } 1463|.endmacro 1464 1465|.macro SET_EX_OPLINE, op, tmp_reg 1466|| if (op == last_valid_opline) { 1467|| zend_jit_use_last_valid_opline(); 1468| SAVE_IP 1469|| } else { 1470| ADDR_STORE EX->opline, op, tmp_reg 1471|| if (!GCC_GLOBAL_REGS) { 1472|| zend_jit_reset_last_valid_opline(); 1473|| } 1474|| } 1475|.endmacro 1476 1477// arg1 "zval" should be in FCARG1x 1478|.macro ZVAL_DTOR_FUNC, var_info, opline, tmp_reg 1479|| do { 1480|| if (!((var_info) & MAY_BE_GUARD) 1481|| && has_concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 1482|| zend_uchar type = concrete_type((var_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); 1483|| if (type == IS_STRING && !ZEND_DEBUG) { 1484| EXT_CALL _efree, tmp_reg 1485|| break; 1486|| } else if (type == IS_ARRAY) { 1487|| 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)) { 1488|| if (opline && ((var_info) & (MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_REF))) { 1489| SET_EX_OPLINE opline, tmp_reg 1490|| } 1491| EXT_CALL zend_array_destroy, tmp_reg 1492|| } else { 1493| EXT_CALL zend_jit_array_free, tmp_reg 1494|| } 1495|| break; 1496|| } else if (type == IS_OBJECT) { 1497|| if (opline) { 1498| SET_EX_OPLINE opline, REG0 1499|| } 1500| EXT_CALL zend_objects_store_del, tmp_reg 1501|| break; 1502|| } 1503|| } 1504|| if (opline) { 1505| SET_EX_OPLINE opline, tmp_reg 1506|| } 1507| EXT_CALL rc_dtor_func, tmp_reg 1508|| } while(0); 1509|.endmacro 1510 1511|.macro ZVAL_PTR_DTOR, addr, op_info, gc, cold, opline, tmp_reg1, tmp_reg2 1512|| if ((op_info) & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF|MAY_BE_GUARD)) { 1513|| if ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 1514| // if (Z_REFCOUNTED_P(cv)) { 1515|| if (cold) { 1516| IF_ZVAL_REFCOUNTED addr, >1, tmp_reg1, tmp_reg2 1517|.cold_code 1518|1: 1519|| } else { 1520| IF_NOT_ZVAL_REFCOUNTED addr, >4, tmp_reg1, tmp_reg2 1521|| } 1522|| } 1523| // if (!Z_DELREF_P(cv)) { 1524| GET_ZVAL_PTR FCARG1x, addr, Rx(tmp_reg2) 1525| GC_DELREF FCARG1x, Rw(tmp_reg1) 1526|| if (((op_info) & MAY_BE_GUARD) || RC_MAY_BE_1(op_info)) { 1527|| if (((op_info) & MAY_BE_GUARD) || RC_MAY_BE_N(op_info)) { 1528|| 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))) { 1529| bne >3 1530|| } else { 1531| bne >4 1532|| } 1533|| } 1534| // zval_dtor_func(r); 1535| ZVAL_DTOR_FUNC op_info, opline, Rx(tmp_reg1) 1536|| 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))) { 1537| b >4 1538|| } 1539|3: 1540|| } 1541|| 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))) { 1542|| if ((op_info) & (MAY_BE_REF|MAY_BE_GUARD)) { 1543|| zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, offsetof(zend_reference, val)); 1544| IF_NOT_ZVAL_TYPE addr, IS_REFERENCE, >1, tmp_reg1 1545| IF_NOT_ZVAL_COLLECTABLE ref_addr, >4, tmp_reg1, tmp_reg2 1546| GET_ZVAL_PTR FCARG1x, ref_addr, Rx(tmp_reg2) 1547|1: 1548|| } 1549| IF_GC_MAY_NOT_LEAK FCARG1x, >4, Rw(tmp_reg1), Rw(tmp_reg2) 1550| // gc_possible_root(Z_COUNTED_P(z)) 1551| EXT_CALL gc_possible_root, Rx(tmp_reg1) 1552|| } 1553|| if (cold && ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) != 0) { 1554| b >4 1555|.code 1556|| } 1557|4: 1558|| } 1559|.endmacro 1560 1561|.macro FREE_OP, op_type, op, op_info, cold, opline, tmp_reg1, tmp_reg2 1562|| if (op_type & (IS_VAR|IS_TMP_VAR)) { 1563|| zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var); 1564| ZVAL_PTR_DTOR addr, op_info, 0, cold, opline, tmp_reg1, tmp_reg2 1565|| } 1566|.endmacro 1567 1568|.macro SEPARATE_ARRAY, addr, op_info, cold, tmp_reg1, tmp_reg2 1569|| if (RC_MAY_BE_N(op_info)) { 1570|| if (Z_REG(addr) != ZREG_FP) { 1571| GET_ZVAL_LVAL ZREG_REG0, addr, Rx(tmp_reg1) 1572|| if (RC_MAY_BE_1(op_info)) { 1573| // if (GC_REFCOUNT() > 1) 1574| ldr Rw(tmp_reg1), [REG0] 1575| cmp Rw(tmp_reg1), #1 1576| bls >2 1577|| } 1578|| if (Z_REG(addr) != ZREG_FCARG1 || Z_OFFSET(addr) != 0) { 1579| LOAD_ZVAL_ADDR FCARG1x, addr 1580|| } 1581| EXT_CALL zend_jit_zval_array_dup, REG0 1582| mov REG0, RETVALx 1583|2: 1584| mov FCARG1x, REG0 1585|| } else { 1586| GET_ZVAL_LVAL ZREG_FCARG1, addr, Rx(tmp_reg1) 1587|| if (RC_MAY_BE_1(op_info)) { 1588| // if (GC_REFCOUNT() > 1) 1589| ldr Rw(tmp_reg1), [FCARG1x] 1590| cmp Rw(tmp_reg1), #1 1591|| if (cold) { 1592| bhi >1 1593|.cold_code 1594|1: 1595|| } else { 1596| bls >2 1597|| } 1598|| } 1599| IF_NOT_ZVAL_REFCOUNTED addr, >1, tmp_reg1, tmp_reg2 1600| GC_DELREF FCARG1x, Rw(tmp_reg1) 1601|1: 1602| EXT_CALL zend_array_dup, REG0 1603| mov REG0, RETVALx 1604| SET_ZVAL_PTR addr, REG0, Rx(tmp_reg1) 1605| SET_ZVAL_TYPE_INFO addr, IS_ARRAY_EX, Rw(tmp_reg1), Rx(tmp_reg2) 1606| mov FCARG1x, REG0 1607|| if (RC_MAY_BE_1(op_info)) { 1608|| if (cold) { 1609| b >2 1610|.code 1611|| } 1612|| } 1613|2: 1614|| } 1615|| } else { 1616| GET_ZVAL_LVAL ZREG_FCARG1, addr, Rx(tmp_reg1) 1617|| } 1618|.endmacro 1619 1620/* argument is passed in FCARG1x */ 1621|.macro EFREE_REFERENCE 1622||#if ZEND_DEBUG 1623| mov FCARG2x, xzr // filename 1624| mov CARG3w, wzr // lineno 1625| mov CARG4, xzr 1626| mov CARG5, xzr 1627| EXT_CALL _efree, REG0 1628||#else 1629||#ifdef HAVE_BUILTIN_CONSTANT_P 1630| EXT_CALL _efree_32, REG0 1631||#else 1632| EXT_CALL _efree, REG0 1633||#endif 1634||#endif 1635|.endmacro 1636 1637|.macro EMALLOC, size, op_array, opline 1638||#if ZEND_DEBUG 1639|| const char *filename = op_array->filename ? op_array->filename->val : NULL; 1640| mov FCARG1x, #size 1641| LOAD_ADDR FCARG2x, filename 1642| LOAD_32BIT_VAL CARG3w, opline->lineno 1643| mov CARG4, xzr 1644| mov CARG5, xzr 1645| EXT_CALL _emalloc, REG0 1646| mov REG0, RETVALx 1647||#else 1648||#ifdef HAVE_BUILTIN_CONSTANT_P 1649|| if (size > 24 && size <= 32) { 1650| EXT_CALL _emalloc_32, REG0 1651| mov REG0, RETVALx 1652|| } else { 1653| mov FCARG1x, #size 1654| EXT_CALL _emalloc, REG0 1655| mov REG0, RETVALx 1656|| } 1657||#else 1658| mov FCARG1x, #size 1659| EXT_CALL _emalloc, REG0 1660| mov REG0, RETVALx 1661||#endif 1662||#endif 1663|.endmacro 1664 1665|.macro OBJ_RELEASE, reg, exit_label, tmp_reg1, tmp_reg2 1666| GC_DELREF Rx(reg), Rw(tmp_reg1) 1667| bne >1 1668| // zend_objects_store_del(obj); 1669|| if (reg != ZREG_FCARG1) { 1670| mov FCARG1x, Rx(reg) 1671|| } 1672| EXT_CALL zend_objects_store_del, Rx(tmp_reg1) 1673| b exit_label 1674|1: 1675| IF_GC_MAY_NOT_LEAK Rx(reg), >1, Rw(tmp_reg1), Rw(tmp_reg2) 1676| // gc_possible_root(obj) 1677|| if (reg != ZREG_FCARG1) { 1678| mov FCARG1x, Rx(reg) 1679|| } 1680| EXT_CALL gc_possible_root, Rx(tmp_reg1) 1681|1: 1682|.endmacro 1683 1684|.macro UNDEFINED_OFFSET, opline 1685|| if (opline == last_valid_opline) { 1686|| zend_jit_use_last_valid_opline(); 1687| bl ->undefined_offset_ex 1688|| } else { 1689| SET_EX_OPLINE opline, REG0 1690| bl ->undefined_offset 1691|| } 1692|.endmacro 1693 1694|.macro UNDEFINED_INDEX, opline 1695|| if (opline == last_valid_opline) { 1696|| zend_jit_use_last_valid_opline(); 1697| bl ->undefined_index_ex 1698|| } else { 1699| SET_EX_OPLINE opline, REG0 1700| bl ->undefined_index 1701|| } 1702|.endmacro 1703 1704|.macro CANNOT_ADD_ELEMENT, opline 1705|| if (opline == last_valid_opline) { 1706|| zend_jit_use_last_valid_opline(); 1707| bl ->cannot_add_element_ex 1708|| } else { 1709| SET_EX_OPLINE opline, REG0 1710| bl ->cannot_add_element 1711|| } 1712|.endmacro 1713 1714static bool reuse_ip = 0; 1715static bool delayed_call_chain = 0; 1716static uint32_t delayed_call_level = 0; 1717static const zend_op *last_valid_opline = NULL; 1718static bool use_last_vald_opline = 0; 1719static bool track_last_valid_opline = 0; 1720static int jit_return_label = -1; 1721static uint32_t current_trace_num = 0; 1722static uint32_t allowed_opt_flags = 0; 1723 1724static void zend_jit_track_last_valid_opline(void) 1725{ 1726 use_last_vald_opline = 0; 1727 track_last_valid_opline = 1; 1728} 1729 1730static void zend_jit_use_last_valid_opline(void) 1731{ 1732 if (track_last_valid_opline) { 1733 use_last_vald_opline = 1; 1734 track_last_valid_opline = 0; 1735 } 1736} 1737 1738static bool zend_jit_trace_uses_initial_ip(void) 1739{ 1740 return use_last_vald_opline; 1741} 1742 1743static void zend_jit_set_last_valid_opline(const zend_op *target_opline) 1744{ 1745 if (!reuse_ip) { 1746 track_last_valid_opline = 0; 1747 last_valid_opline = target_opline; 1748 } 1749} 1750 1751static void zend_jit_reset_last_valid_opline(void) 1752{ 1753 track_last_valid_opline = 0; 1754 last_valid_opline = NULL; 1755} 1756 1757static void zend_jit_start_reuse_ip(void) 1758{ 1759 zend_jit_reset_last_valid_opline(); 1760 reuse_ip = 1; 1761} 1762 1763static int zend_jit_reuse_ip(dasm_State **Dst) 1764{ 1765 if (!reuse_ip) { 1766 zend_jit_start_reuse_ip(); 1767 | // call = EX(call); 1768 | ldr RX, EX->call 1769 } 1770 return 1; 1771} 1772 1773static void zend_jit_stop_reuse_ip(void) 1774{ 1775 reuse_ip = 0; 1776} 1777 1778static int zend_jit_interrupt_handler_stub(dasm_State **Dst) 1779{ 1780 |->interrupt_handler: 1781 | SAVE_IP 1782 | //EG(vm_interrupt) = 0; 1783 | MEM_STORE_8_ZTS strb, wzr, executor_globals, vm_interrupt, TMP1 1784 | //if (EG(timed_out)) { 1785 | MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, timed_out, TMP1 1786 | cbz TMP1w, >1 1787 | //zend_timeout(); 1788 | EXT_CALL zend_timeout, TMP1 1789 |1: 1790 | //} else if (zend_interrupt_function) { 1791 if (zend_interrupt_function) { 1792 | //zend_interrupt_function(execute_data); 1793 | mov CARG1, FP 1794 | EXT_CALL zend_interrupt_function, TMP1 1795 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 1796 | cbz REG0, >1 1797 | EXT_CALL zend_jit_exception_in_interrupt_handler_helper, TMP1 1798 |1: 1799 | //ZEND_VM_ENTER(); 1800 | //execute_data = EG(current_execute_data); 1801 | MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, TMP1 1802 | LOAD_IP 1803 } 1804 | //ZEND_VM_CONTINUE() 1805 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 1806 | ADD_HYBRID_SPAD 1807 | JMP_IP TMP1 1808 } else if (GCC_GLOBAL_REGS) { 1809 | ldp x29, x30, [sp], # SPAD // stack alignment 1810 | JMP_IP TMP1 1811 } else { 1812 | ldp FP, RX, T2 // restore FP and IP 1813 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 1814 | mov RETVALx, #1 // ZEND_VM_ENTER 1815 | ret 1816 } 1817 1818 return 1; 1819} 1820 1821static int zend_jit_exception_handler_stub(dasm_State **Dst) 1822{ 1823 |->exception_handler: 1824 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 1825 const void *handler = zend_get_opcode_handler_func(EG(exception_op)); 1826 1827 | ADD_HYBRID_SPAD 1828 | EXT_CALL handler, REG0 1829 | JMP_IP TMP1 1830 } else { 1831 const void *handler = EG(exception_op)->handler; 1832 1833 if (GCC_GLOBAL_REGS) { 1834 | ldp x29, x30, [sp], # SPAD // stack alignment 1835 | EXT_JMP handler, REG0 1836 } else { 1837 | mov FCARG1x, FP 1838 | EXT_CALL handler, REG0 1839 | ldp FP, RX, T2 // restore FP and IP 1840 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 1841 | tbnz RETVALw, #31, >1 1842 | mov RETVALw, #1 // ZEND_VM_ENTER 1843 |1: 1844 | ret 1845 } 1846 } 1847 1848 return 1; 1849} 1850 1851static int zend_jit_exception_handler_undef_stub(dasm_State **Dst) 1852{ 1853 |->exception_handler_undef: 1854 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, opline_before_exception, REG0 1855 | ldrb TMP1w, OP:REG0->result_type 1856 | TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w 1857 | beq >1 1858 | ldr REG0w, OP:REG0->result.var 1859 | add REG0, REG0, FP 1860 | SET_Z_TYPE_INFO REG0, IS_UNDEF, TMP1w 1861 |1: 1862 | b ->exception_handler 1863 1864 return 1; 1865} 1866 1867static int zend_jit_exception_handler_free_op1_op2_stub(dasm_State **Dst) 1868{ 1869 zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 1870 1871 |->exception_handler_free_op1_op2: 1872 | UNDEF_OPLINE_RESULT_IF_USED TMP1w, TMP2w 1873 | ldrb TMP1w, OP:RX->op1_type 1874 | TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w 1875 | beq >9 1876 | ldr REG0w, OP:RX->op1.var 1877 | add REG0, REG0, FP 1878 | ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2 1879 |9: 1880 | ldrb TMP1w, OP:RX->op2_type 1881 | TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w 1882 | beq >9 1883 | ldr REG0w, OP:RX->op2.var 1884 | add REG0, REG0, FP 1885 | ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2 1886 |9: 1887 | b ->exception_handler 1888 return 1; 1889} 1890 1891static int zend_jit_exception_handler_free_op2_stub(dasm_State **Dst) 1892{ 1893 zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 1894 1895 |->exception_handler_free_op2: 1896 | MEM_LOAD_64_ZTS ldr, RX, executor_globals, opline_before_exception, REG0 1897 | UNDEF_OPLINE_RESULT_IF_USED TMP1w, TMP2w 1898 | ldrb TMP1w, OP:RX->op2_type 1899 | TST_32_WITH_CONST TMP1w, (IS_TMP_VAR|IS_VAR), TMP2w 1900 | beq >9 1901 | ldr REG0w, OP:RX->op2.var 1902 | add REG0, REG0, FP 1903 | ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2 1904 |9: 1905 | b ->exception_handler 1906 return 1; 1907} 1908 1909static int zend_jit_leave_function_stub(dasm_State **Dst) 1910{ 1911 |->leave_function_handler: 1912 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 1913 | TST_32_WITH_CONST FCARG1w, ZEND_CALL_TOP, TMP1w 1914 | bne >1 1915 | EXT_CALL zend_jit_leave_nested_func_helper, REG0 1916 | ADD_HYBRID_SPAD 1917 | JMP_IP TMP1 1918 |1: 1919 | EXT_CALL zend_jit_leave_top_func_helper, REG0 1920 | ADD_HYBRID_SPAD 1921 | JMP_IP TMP1 1922 } else { 1923 if (GCC_GLOBAL_REGS) { 1924 | ldp x29, x30, [sp], # SPAD // stack alignment 1925 } else { 1926 | mov FCARG2x, FP 1927 | ldp FP, RX, T2 // restore FP and IP 1928 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 1929 } 1930 | TST_32_WITH_CONST FCARG1w, ZEND_CALL_TOP, TMP1w 1931 | bne >1 1932 | EXT_JMP zend_jit_leave_nested_func_helper, REG0 1933 |1: 1934 | EXT_JMP zend_jit_leave_top_func_helper, REG0 1935 } 1936 1937 return 1; 1938} 1939 1940static int zend_jit_leave_throw_stub(dasm_State **Dst) 1941{ 1942 |->leave_throw_handler: 1943 | // if (opline->opcode != ZEND_HANDLE_EXCEPTION) { 1944 if (GCC_GLOBAL_REGS) { 1945 | ldrb TMP1w, OP:IP->opcode 1946 | cmp TMP1w, #ZEND_HANDLE_EXCEPTION 1947 | beq >5 1948 | // EG(opline_before_exception) = opline; 1949 | MEM_STORE_64_ZTS str, IP, executor_globals, opline_before_exception, TMP2 1950 |5: 1951 | // opline = EG(exception_op); 1952 | LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2 1953 | str IP, EX->opline 1954 | // HANDLE_EXCEPTION() 1955 | b ->exception_handler 1956 } else { 1957 | GET_IP TMP1 1958 | ldrb TMP2w, OP:TMP1->opcode 1959 | cmp TMP2w, #ZEND_HANDLE_EXCEPTION 1960 | beq >5 1961 | // EG(opline_before_exception) = opline; 1962 | MEM_STORE_64_ZTS str, TMP1, executor_globals, opline_before_exception, TMP2 1963 |5: 1964 | // opline = EG(exception_op); 1965 | LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2 1966 | ldp FP, RX, T2 // restore FP and IP 1967 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 1968 | mov RETVALx, #2 // ZEND_VM_LEAVE 1969 | ret 1970 } 1971 1972 return 1; 1973} 1974 1975static int zend_jit_icall_throw_stub(dasm_State **Dst) 1976{ 1977 |->icall_throw_handler: 1978 | // zend_rethrow_exception(zend_execute_data *execute_data) 1979 | ldr IP, EX->opline 1980 | // if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) { 1981 | ldrb TMP1w, OP:IP->opcode 1982 | cmp TMP1w, #ZEND_HANDLE_EXCEPTION 1983 | beq >1 1984 | // EG(opline_before_exception) = opline; 1985 | MEM_STORE_64_ZTS str, IP, executor_globals, opline_before_exception, TMP2 1986 |1: 1987 | // opline = EG(exception_op); 1988 | LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2 1989 || if (GCC_GLOBAL_REGS) { 1990 | str IP, EX->opline 1991 || } 1992 | // HANDLE_EXCEPTION() 1993 | b ->exception_handler 1994 1995 return 1; 1996} 1997 1998static int zend_jit_throw_cannot_pass_by_ref_stub(dasm_State **Dst) 1999{ 2000 zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 2001 2002 |->throw_cannot_pass_by_ref: 2003 | ldr REG0, EX->opline 2004 | ldr REG1w, OP:REG0->result.var 2005 | add REG1, REG1, RX 2006 | SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w 2007 | // last EX(call) frame may be delayed 2008 | ldr TMP1, EX->call 2009 | cmp RX, TMP1 2010 | beq >1 2011 | ldr REG1, EX->call 2012 | str REG1, EX:RX->prev_execute_data 2013 | str RX, EX->call 2014 |1: 2015 | mov RX, REG0 2016 | ldr FCARG1w, OP:REG0->op2.num 2017 | EXT_CALL zend_cannot_pass_by_reference, REG0 2018 | ldrb TMP1w, OP:RX->op1_type 2019 | cmp TMP1w, #IS_TMP_VAR 2020 | bne >9 2021 | ldr REG0w, OP:RX->op1.var 2022 | add REG0, REG0, FP 2023 | ZVAL_PTR_DTOR addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL, ZREG_TMP1, ZREG_TMP2 2024 |9: 2025 | b ->exception_handler 2026 2027 return 1; 2028} 2029 2030static int zend_jit_undefined_offset_ex_stub(dasm_State **Dst) 2031{ 2032 |->undefined_offset_ex: 2033 | SAVE_IP 2034 | b ->undefined_offset 2035 2036 return 1; 2037} 2038 2039static int zend_jit_undefined_offset_stub(dasm_State **Dst) 2040{ 2041 |->undefined_offset: 2042 || if (!GCC_GLOBAL_REGS) { 2043 | mov FCARG1x, FP 2044 || } 2045 | EXT_JMP zend_jit_undefined_long_key, REG0 2046 2047 return 1; 2048} 2049 2050static int zend_jit_undefined_index_ex_stub(dasm_State **Dst) 2051{ 2052 |->undefined_index_ex: 2053 | SAVE_IP 2054 | b ->undefined_index 2055 2056 return 1; 2057} 2058 2059static int zend_jit_undefined_index_stub(dasm_State **Dst) 2060{ 2061 |->undefined_index: 2062 || if (!GCC_GLOBAL_REGS) { 2063 | mov FCARG1x, FP 2064 || } 2065 | EXT_JMP zend_jit_undefined_string_key, REG0 2066 2067 return 1; 2068} 2069 2070static int zend_jit_cannot_add_element_ex_stub(dasm_State **Dst) 2071{ 2072 |->cannot_add_element_ex: 2073 | SAVE_IP 2074 | b ->cannot_add_element 2075 2076 return 1; 2077} 2078 2079static int zend_jit_cannot_add_element_stub(dasm_State **Dst) 2080{ 2081 |->cannot_add_element: 2082 | // sub r4, 8 2083 | ldr REG0, EX->opline 2084 | ldrb TMP1w, OP:REG0->result_type 2085 | cmp TMP1w, #IS_UNUSED 2086 | beq >1 2087 | ldr REG0w, OP:REG0->result.var 2088 | add REG0, REG0, FP 2089 | SET_Z_TYPE_INFO REG0, IS_NULL, TMP1w 2090 |1: 2091 | mov CARG1, xzr 2092 | LOAD_ADDR CARG2, "Cannot add element to the array as the next element is already occupied" 2093 | EXT_JMP zend_throw_error, REG0 // tail call 2094 | // add r4, 8 2095 | //ret 2096 2097 return 1; 2098} 2099 2100static int zend_jit_undefined_function_stub(dasm_State **Dst) 2101{ 2102 |->undefined_function: 2103 | ldr REG0, EX->opline 2104 | mov CARG1, xzr 2105 | LOAD_ADDR CARG2, "Call to undefined function %s()" 2106 | ldrsw CARG3, [REG0, #offsetof(zend_op, op2.constant)] 2107 | ldr CARG3, [REG0, CARG3] 2108 | add CARG3, CARG3, #offsetof(zend_string, val) 2109#ifdef __APPLE__ 2110 | str CARG3, [sp, #-16]! 2111#endif 2112 | EXT_CALL zend_throw_error, REG0 2113#ifdef __APPLE__ 2114 | add sp, sp, #16 2115#endif 2116 | b ->exception_handler 2117 return 1; 2118} 2119 2120static int zend_jit_negative_shift_stub(dasm_State **Dst) 2121{ 2122 |->negative_shift: 2123 | ldr RX, EX->opline 2124 | LOAD_ADDR CARG1, zend_ce_arithmetic_error 2125 | LOAD_ADDR CARG2, "Bit shift by negative number" 2126 | EXT_CALL zend_throw_error, REG0 2127 | b ->exception_handler_free_op1_op2 2128 return 1; 2129} 2130 2131static int zend_jit_mod_by_zero_stub(dasm_State **Dst) 2132{ 2133 |->mod_by_zero: 2134 | ldr RX, EX->opline 2135 | LOAD_ADDR CARG1, zend_ce_division_by_zero_error 2136 | LOAD_ADDR CARG2, "Modulo by zero" 2137 | EXT_CALL zend_throw_error, REG0 2138 | b ->exception_handler_free_op1_op2 2139 return 1; 2140} 2141 2142static int zend_jit_invalid_this_stub(dasm_State **Dst) 2143{ 2144 |->invalid_this: 2145 | UNDEF_OPLINE_RESULT TMP1w 2146 | mov CARG1, xzr 2147 | LOAD_ADDR CARG2, "Using $this when not in object context" 2148 | EXT_CALL zend_throw_error, REG0 2149 | b ->exception_handler 2150 2151 return 1; 2152} 2153 2154static int zend_jit_hybrid_runtime_jit_stub(dasm_State **Dst) 2155{ 2156 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) { 2157 return 1; 2158 } 2159 2160 |->hybrid_runtime_jit: 2161 | EXT_CALL zend_runtime_jit, REG0 2162 | JMP_IP TMP1 2163 return 1; 2164} 2165 2166static int zend_jit_hybrid_profile_jit_stub(dasm_State **Dst) 2167{ 2168 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) { 2169 return 1; 2170 } 2171 2172 |->hybrid_profile_jit: 2173 | // ++zend_jit_profile_counter; 2174 | LOAD_ADDR REG0, &zend_jit_profile_counter 2175 | ldr TMP1, [REG0] 2176 | add TMP1, TMP1, #1 2177 | str TMP1, [REG0] 2178 | // op_array = (zend_op_array*)EX(func); 2179 | ldr REG0, EX->func 2180 | // run_time_cache = EX(run_time_cache); 2181 | ldr REG2, EX->run_time_cache 2182 | // jit_extension = (const void*)ZEND_FUNC_INFO(op_array); 2183 | ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 2184 | // ++ZEND_COUNTER_INFO(op_array) 2185 || if ((zend_jit_profile_counter_rid * sizeof(void*)) > LDR_STR_PIMM64) { 2186 | LOAD_32BIT_VAL TMP1, (zend_jit_profile_counter_rid * sizeof(void*)) 2187 | ldr TMP2, [REG2, TMP1] 2188 | add TMP2, TMP2, #1 2189 | str TMP2, [REG2, TMP1] 2190 || } else { 2191 | ldr TMP2, [REG2, #(zend_jit_profile_counter_rid * sizeof(void*))] 2192 | add TMP2, TMP2, #1 2193 | str TMP2, [REG2, #(zend_jit_profile_counter_rid * sizeof(void*))] 2194 || } 2195 | // return ((zend_vm_opcode_handler_t)jit_extension->orig_handler)() 2196 | ldr TMP1, [REG0, #offsetof(zend_jit_op_array_extension, orig_handler)] 2197 | br TMP1 2198 return 1; 2199} 2200 2201static int zend_jit_hybrid_hot_code_stub(dasm_State **Dst) 2202{ 2203 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) { 2204 return 1; 2205 } 2206 2207 |->hybrid_hot_code: 2208 || ZEND_ASSERT(ZEND_JIT_COUNTER_INIT <= MOVZ_IMM); 2209 | movz TMP1w, #ZEND_JIT_COUNTER_INIT 2210 | strh TMP1w, [REG2] 2211 | mov FCARG1x, FP 2212 | GET_IP FCARG2x 2213 | EXT_CALL zend_jit_hot_func, REG0 2214 | JMP_IP TMP1 2215 return 1; 2216} 2217 2218/* 2219 * This code is based Mike Pall's "Hashed profile counters" idea, implemented 2220 * in LuaJIT. The full description may be found in "LuaJIT 2.0 intellectual 2221 * property disclosure and research opportunities" email 2222 * at http://lua-users.org/lists/lua-l/2009-11/msg00089.html 2223 * 2224 * In addition we use a variation of Knuth's multiplicative hash function 2225 * described at https://code.i-harness.com/en/q/a21ce 2226 * 2227 * uint64_t hash(uint64_t x) { 2228 * x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; 2229 * x = (x ^ (x >> 27)) * 0x94d049bb133111eb; 2230 * x = x ^ (x >> 31); 2231 * return x; 2232 * } 2233 * 2234 * uint_32_t hash(uint32_t x) { 2235 * x = ((x >> 16) ^ x) * 0x45d9f3b; 2236 * x = ((x >> 16) ^ x) * 0x45d9f3b; 2237 * x = (x >> 16) ^ x; 2238 * return x; 2239 * } 2240 * 2241 */ 2242static int zend_jit_hybrid_hot_counter_stub(dasm_State **Dst, uint32_t cost) 2243{ 2244 | ldr REG0, EX->func 2245 | ldr REG1, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 2246 | ldr REG2, [REG1, #offsetof(zend_jit_op_array_hot_extension, counter)] 2247 | ldrh TMP2w, [REG2] 2248 | ADD_SUB_32_WITH_CONST subs, TMP2w, TMP2w, cost, TMP1w 2249 | strh TMP2w, [REG2] 2250 | ble ->hybrid_hot_code 2251 | GET_IP REG2 2252 | ldr TMP1, [REG0, #offsetof(zend_op_array, opcodes)] 2253 | sub REG2, REG2, TMP1 2254 | // divide by sizeof(zend_op) 2255 || ZEND_ASSERT(sizeof(zend_op) == 32); 2256 | add TMP1, REG1, REG2, asr #2 2257 | ldr TMP1, [TMP1, #offsetof(zend_jit_op_array_hot_extension, orig_handlers)] 2258 | br TMP1 2259 return 1; 2260} 2261 2262static int zend_jit_hybrid_func_hot_counter_stub(dasm_State **Dst) 2263{ 2264 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) { 2265 return 1; 2266 } 2267 2268 |->hybrid_func_hot_counter: 2269 2270 return zend_jit_hybrid_hot_counter_stub(Dst, 2271 ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func))); 2272} 2273 2274static int zend_jit_hybrid_loop_hot_counter_stub(dasm_State **Dst) 2275{ 2276 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) { 2277 return 1; 2278 } 2279 2280 |->hybrid_loop_hot_counter: 2281 2282 return zend_jit_hybrid_hot_counter_stub(Dst, 2283 ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop))); 2284} 2285 2286static int zend_jit_hybrid_hot_trace_stub(dasm_State **Dst) 2287{ 2288 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) { 2289 return 1; 2290 } 2291 2292 // On entry from counter stub: 2293 // REG2 -> zend_op_trace_info.counter 2294 2295 |->hybrid_hot_trace: 2296 | mov TMP1w, #ZEND_JIT_COUNTER_INIT 2297 | strh TMP1w, [REG2] 2298 | mov FCARG1x, FP 2299 | GET_IP FCARG2x 2300 | EXT_CALL zend_jit_trace_hot_root, REG0 2301 | tbnz RETVALw, #31, >1 // Result is < 0 on failure. 2302 | MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, REG0 2303 | LOAD_IP 2304 | JMP_IP TMP1 2305 |1: 2306 | EXT_JMP zend_jit_halt_op->handler, REG0 2307 2308 return 1; 2309} 2310 2311static int zend_jit_hybrid_trace_counter_stub(dasm_State **Dst, uint32_t cost) 2312{ 2313 | ldr REG0, EX->func 2314 | ldr REG1, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 2315 | ldr REG1, [REG1, #offsetof(zend_jit_op_array_trace_extension, offset)] 2316 | add TMP1, REG1, IP 2317 | ldr REG2, [TMP1, #offsetof(zend_op_trace_info, counter)] 2318 | ldrh TMP2w, [REG2] 2319 | ADD_SUB_32_WITH_CONST subs, TMP2w, TMP2w, cost, TMP1w 2320 | strh TMP2w, [REG2] 2321 | ble ->hybrid_hot_trace 2322 // Note: "REG1 + IP" is re-calculated as TMP1 is used as temporary register by the prior 2323 // ADD_SUB_32_WITH_CONST. Will optimize in the future if more temporary registers are available. 2324 | add TMP1, REG1, IP 2325 | ldr TMP2, [TMP1, #offsetof(zend_op_trace_info, orig_handler)] 2326 | br TMP2 2327 2328 return 1; 2329} 2330 2331static int zend_jit_hybrid_func_trace_counter_stub(dasm_State **Dst) 2332{ 2333 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) { 2334 return 1; 2335 } 2336 2337 |->hybrid_func_trace_counter: 2338 2339 return zend_jit_hybrid_trace_counter_stub(Dst, 2340 ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_func) - 1) / JIT_G(hot_func))); 2341} 2342 2343static int zend_jit_hybrid_ret_trace_counter_stub(dasm_State **Dst) 2344{ 2345 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_return)) { 2346 return 1; 2347 } 2348 2349 |->hybrid_ret_trace_counter: 2350 2351 return zend_jit_hybrid_trace_counter_stub(Dst, 2352 ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_return) - 1) / JIT_G(hot_return))); 2353} 2354 2355static int zend_jit_hybrid_loop_trace_counter_stub(dasm_State **Dst) 2356{ 2357 if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) { 2358 return 1; 2359 } 2360 2361 |->hybrid_loop_trace_counter: 2362 2363 return zend_jit_hybrid_trace_counter_stub(Dst, 2364 ((ZEND_JIT_COUNTER_INIT + JIT_G(hot_loop) - 1) / JIT_G(hot_loop))); 2365} 2366 2367static int zend_jit_trace_halt_stub(dasm_State **Dst) 2368{ 2369 |->trace_halt: 2370 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 2371 | ADD_HYBRID_SPAD 2372 | EXT_JMP zend_jit_halt_op->handler, REG0 2373 } else if (GCC_GLOBAL_REGS) { 2374 | ldp x29, x30, [sp], # SPAD // stack alignment 2375 | mov IP, xzr // PC must be zero 2376 | ret 2377 } else { 2378 | ldp FP, RX, T2 // restore FP and IP 2379 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 2380 | movn RETVALx, #0 // ZEND_VM_RETURN (-1) 2381 | ret 2382 } 2383 return 1; 2384} 2385 2386static int zend_jit_trace_exit_stub(dasm_State **Dst) 2387{ 2388 |->trace_exit: 2389 | 2390 | // Save CPU registers(32 GP regs + 32 FP regs) on stack in the order of d31 to x0 2391 | 2392 | stp d30, d31, [sp, #-16]! 2393 | stp d28, d29, [sp, #-16]! 2394 | stp d26, d27, [sp, #-16]! 2395 | stp d24, d25, [sp, #-16]! 2396 | stp d22, d23, [sp, #-16]! 2397 | stp d20, d21, [sp, #-16]! 2398 | stp d18, d19, [sp, #-16]! 2399 | stp d16, d17, [sp, #-16]! 2400 | //stp d14, d15, [sp, #-16]! // we don't use preserved registers yet 2401 | //stp d12, d13, [sp, #-16]! 2402 | //stp d10, d11, [sp, #-16]! 2403 | //stp d8, d9, [sp, #-16]! 2404 | stp d6, d7, [sp, #(-16*5)]! 2405 | stp d4, d5, [sp, #-16]! 2406 | stp d2, d3, [sp, #-16]! 2407 | stp d0, d1, [sp, #-16]! 2408 | 2409 | //str x30, [sp, #-16]! // we don't use callee-saved registers yet (x31 can be omitted) 2410 | stp x28, x29, [sp, #(-16*2)]! // we have to store RX (x28) 2411 | //stp x26, x27, [sp, #-16]! // we don't use callee-saved registers yet 2412 | //stp x24, x25, [sp, #-16]! 2413 | //stp x22, x23, [sp, #-16]! 2414 | //stp x20, x21, [sp, #-16]! 2415 | //stp x18, x19, [sp, #-16]! 2416 | //stp x16, x17, [sp, #-16]! // we don't need temporary registers 2417 | stp x14, x15, [sp, #-(16*7)]! 2418 | stp x12, x13, [sp, #-16]! 2419 | stp x10, x11, [sp, #-16]! 2420 | stp x8, x9, [sp, #-16]! 2421 | stp x6, x7, [sp, #-16]! 2422 | stp x4, x5, [sp, #-16]! 2423 | stp x2, x3, [sp, #-16]! 2424 | stp x0, x1, [sp, #-16]! 2425 | 2426 | mov FCARG1w, TMP1w // exit_num 2427 | mov FCARG2x, sp 2428 | 2429 | // EX(opline) = opline 2430 | SAVE_IP 2431 | // zend_jit_trace_exit(trace_num, exit_num) 2432 | EXT_CALL zend_jit_trace_exit, REG0 2433 | 2434 | add sp, sp, #(32 * 16) // restore sp 2435 | 2436 2437 | tst RETVALw, RETVALw 2438 | bne >1 // not zero 2439 2440 | // execute_data = EG(current_execute_data) 2441 | MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, REG0 2442 | // opline = EX(opline) 2443 | LOAD_IP 2444 2445 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 2446 | ADD_HYBRID_SPAD 2447 | JMP_IP TMP1 2448 } else if (GCC_GLOBAL_REGS) { 2449 | ldp x29, x30, [sp], # SPAD // stack alignment 2450 | JMP_IP TMP1 2451 } else { 2452 | ldp FP, RX, T2 // restore FP and IP 2453 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 2454 | mov RETVALx, #1 // ZEND_VM_ENTER 2455 | ret 2456 } 2457 2458 |1: 2459 | blt ->trace_halt 2460 2461 | // execute_data = EG(current_execute_data) 2462 | MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, REG0 2463 | // opline = EX(opline) 2464 | LOAD_IP 2465 2466 | // check for interrupt (try to avoid this ???) 2467 | MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1 2468 | cbnz TMP1w, ->interrupt_handler 2469 2470 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 2471 | ADD_HYBRID_SPAD 2472 | ldr REG0, EX->func 2473 | ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 2474 | ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)] 2475 | ldr REG0, [IP, REG0] 2476 | br REG0 2477 } else if (GCC_GLOBAL_REGS) { 2478 | ldp x29, x30, [sp], # SPAD // stack alignment 2479 | ldr REG0, EX->func 2480 | ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 2481 | ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)] 2482 | ldr REG0, [IP, REG0] 2483 | br REG0 2484 } else { 2485 | ldr IP, EX->opline 2486 | mov FCARG1x, FP 2487 | ldr REG0, EX->func 2488 | ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 2489 | ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)] 2490 | ldr REG0, [IP, REG0] 2491 | blr REG0 2492 | 2493 | tst RETVALw, RETVALw 2494 | blt ->trace_halt 2495 | 2496 | ldp FP, RX, T2 // restore FP and IP 2497 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 2498 | mov RETVALx, #1 // ZEND_VM_ENTER 2499 | ret 2500 } 2501 2502 return 1; 2503} 2504 2505static int zend_jit_trace_escape_stub(dasm_State **Dst) 2506{ 2507 |->trace_escape: 2508 | 2509 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 2510 | ADD_HYBRID_SPAD 2511 | JMP_IP, TMP1 2512 } else if (GCC_GLOBAL_REGS) { 2513 | ldp x29, x30, [sp], # SPAD // stack alignment 2514 | JMP_IP, TMP1 2515 } else { 2516 | ldp FP, RX, T2 // restore FP and IP 2517 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 2518 | mov RETVALx, #1 // ZEND_VM_ENTER 2519 | ret 2520 } 2521 2522 return 1; 2523} 2524 2525/* Keep 32 exit points in a single code block */ 2526#define ZEND_JIT_EXIT_POINTS_SPACING 4 // bl = bytes 2527#define ZEND_JIT_EXIT_POINTS_PER_GROUP 32 // number of continuous exit points 2528 2529static int zend_jit_trace_exit_group_stub(dasm_State **Dst, uint32_t n) 2530{ 2531 uint32_t i; 2532 2533 | bl >2 2534 |1: 2535 for (i = 1; i < ZEND_JIT_EXIT_POINTS_PER_GROUP; i++) { 2536 | bl >2 2537 } 2538 |2: 2539 | adr TMP1, <1 2540 | sub TMP1, lr, TMP1 2541 | lsr TMP1, TMP1, #2 2542 if (n) { 2543 | ADD_SUB_32_WITH_CONST add, TMP1w, TMP1w, n, TMP2w 2544 } 2545 | b ->trace_exit // pass exit_num in TMP1w 2546 2547 return 1; 2548} 2549 2550#ifdef CONTEXT_THREADED_JIT 2551static int zend_jit_context_threaded_call_stub(dasm_State **Dst) 2552{ 2553 |->context_threaded_call: 2554 | NIY_STUB // TODO 2555 return 1; 2556} 2557#endif 2558 2559static int zend_jit_assign_const_stub(dasm_State **Dst) 2560{ 2561 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 2562 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0); 2563 uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN; 2564 2565 |->assign_const: 2566 | stp x29, x30, [sp, #-32]! 2567 | mov x29, sp 2568 if (!zend_jit_assign_to_variable( 2569 Dst, NULL, 2570 var_addr, var_addr, -1, -1, 2571 IS_CONST, val_addr, val_info, 2572 0, 0)) { 2573 return 0; 2574 } 2575 | ldp x29, x30, [sp], #32 2576 | ret 2577 return 1; 2578} 2579 2580static int zend_jit_assign_tmp_stub(dasm_State **Dst) 2581{ 2582 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 2583 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0); 2584 uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN; 2585 2586 |->assign_tmp: 2587 | stp x29, x30, [sp, #-32]! 2588 | mov x29, sp 2589 if (!zend_jit_assign_to_variable( 2590 Dst, NULL, 2591 var_addr, var_addr, -1, -1, 2592 IS_TMP_VAR, val_addr, val_info, 2593 0, 0)) { 2594 return 0; 2595 } 2596 | ldp x29, x30, [sp], #32 2597 | ret 2598 return 1; 2599} 2600 2601static int zend_jit_assign_var_stub(dasm_State **Dst) 2602{ 2603 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 2604 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0); 2605 uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF; 2606 2607 |->assign_var: 2608 | stp x29, x30, [sp, #-32]! 2609 | mov x29, sp 2610 if (!zend_jit_assign_to_variable( 2611 Dst, NULL, 2612 var_addr, var_addr, -1, -1, 2613 IS_VAR, val_addr, val_info, 2614 0, 0)) { 2615 return 0; 2616 } 2617 | ldp x29, x30, [sp], #32 2618 | ret 2619 return 1; 2620} 2621 2622static int zend_jit_assign_cv_noref_stub(dasm_State **Dst) 2623{ 2624 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 2625 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0); 2626 uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN/*|MAY_BE_UNDEF*/; 2627 2628 |->assign_cv_noref: 2629 | stp x29, x30, [sp, #-32]! 2630 | mov x29, sp 2631 if (!zend_jit_assign_to_variable( 2632 Dst, NULL, 2633 var_addr, var_addr, -1, -1, 2634 IS_CV, val_addr, val_info, 2635 0, 0)) { 2636 return 0; 2637 } 2638 | ldp x29, x30, [sp], #32 2639 | ret 2640 return 1; 2641} 2642 2643static int zend_jit_assign_cv_stub(dasm_State **Dst) 2644{ 2645 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 2646 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0); 2647 uint32_t val_info = MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF/*|MAY_BE_UNDEF*/; 2648 2649 |->assign_cv: 2650 | stp x29, x30, [sp, #-32]! 2651 | mov x29, sp 2652 if (!zend_jit_assign_to_variable( 2653 Dst, NULL, 2654 var_addr, var_addr, -1, -1, 2655 IS_CV, val_addr, val_info, 2656 0, 0)) { 2657 return 0; 2658 } 2659 | ldp x29, x30, [sp], #32 2660 | ret 2661 return 1; 2662} 2663 2664static const zend_jit_stub zend_jit_stubs[] = { 2665 JIT_STUB(interrupt_handler, SP_ADJ_JIT, SP_ADJ_VM), 2666 JIT_STUB(exception_handler, SP_ADJ_JIT, SP_ADJ_VM), 2667 JIT_STUB(exception_handler_undef, SP_ADJ_JIT, SP_ADJ_VM), 2668 JIT_STUB(exception_handler_free_op1_op2, SP_ADJ_JIT, SP_ADJ_VM), 2669 JIT_STUB(exception_handler_free_op2, SP_ADJ_JIT, SP_ADJ_VM), 2670 JIT_STUB(leave_function, SP_ADJ_JIT, SP_ADJ_VM), 2671 JIT_STUB(leave_throw, SP_ADJ_JIT, SP_ADJ_VM), 2672 JIT_STUB(icall_throw, SP_ADJ_JIT, SP_ADJ_VM), 2673 JIT_STUB(throw_cannot_pass_by_ref, SP_ADJ_JIT, SP_ADJ_VM), 2674 JIT_STUB(undefined_offset, SP_ADJ_JIT, SP_ADJ_VM), 2675 JIT_STUB(undefined_index, SP_ADJ_JIT, SP_ADJ_VM), 2676 JIT_STUB(cannot_add_element, SP_ADJ_JIT, SP_ADJ_VM), 2677 JIT_STUB(undefined_offset_ex, SP_ADJ_JIT, SP_ADJ_VM), 2678 JIT_STUB(undefined_index_ex, SP_ADJ_JIT, SP_ADJ_VM), 2679 JIT_STUB(cannot_add_element_ex, SP_ADJ_JIT, SP_ADJ_VM), 2680 JIT_STUB(undefined_function, SP_ADJ_JIT, SP_ADJ_VM), 2681 JIT_STUB(negative_shift, SP_ADJ_JIT, SP_ADJ_VM), 2682 JIT_STUB(mod_by_zero, SP_ADJ_JIT, SP_ADJ_VM), 2683 JIT_STUB(invalid_this, SP_ADJ_JIT, SP_ADJ_VM), 2684 JIT_STUB(trace_halt, SP_ADJ_JIT, SP_ADJ_VM), 2685 JIT_STUB(trace_exit, SP_ADJ_JIT, SP_ADJ_VM), 2686 JIT_STUB(trace_escape, SP_ADJ_JIT, SP_ADJ_VM), 2687 JIT_STUB(hybrid_runtime_jit, SP_ADJ_VM, SP_ADJ_NONE), 2688 JIT_STUB(hybrid_profile_jit, SP_ADJ_VM, SP_ADJ_NONE), 2689 JIT_STUB(hybrid_hot_code, SP_ADJ_VM, SP_ADJ_NONE), 2690 JIT_STUB(hybrid_func_hot_counter, SP_ADJ_VM, SP_ADJ_NONE), 2691 JIT_STUB(hybrid_loop_hot_counter, SP_ADJ_VM, SP_ADJ_NONE), 2692 JIT_STUB(hybrid_hot_trace, SP_ADJ_VM, SP_ADJ_NONE), 2693 JIT_STUB(hybrid_func_trace_counter, SP_ADJ_VM, SP_ADJ_NONE), 2694 JIT_STUB(hybrid_ret_trace_counter, SP_ADJ_VM, SP_ADJ_NONE), 2695 JIT_STUB(hybrid_loop_trace_counter, SP_ADJ_VM, SP_ADJ_NONE), 2696 JIT_STUB(assign_const, SP_ADJ_RET, SP_ADJ_ASSIGN), 2697 JIT_STUB(assign_tmp, SP_ADJ_RET, SP_ADJ_ASSIGN), 2698 JIT_STUB(assign_var, SP_ADJ_RET, SP_ADJ_ASSIGN), 2699 JIT_STUB(assign_cv_noref, SP_ADJ_RET, SP_ADJ_ASSIGN), 2700 JIT_STUB(assign_cv, SP_ADJ_RET, SP_ADJ_ASSIGN), 2701#ifdef CONTEXT_THREADED_JIT 2702 JIT_STUB(context_threaded_call, SP_ADJ_NONE, SP_ADJ_NONE), 2703#endif 2704}; 2705 2706#ifdef HAVE_GDB 2707# if 0 2708typedef struct _Unwind_Context _Unwind_Context; 2709typedef int (*_Unwind_Trace_Fn)(_Unwind_Context *, void *); 2710extern int _Unwind_Backtrace(_Unwind_Trace_Fn, void *); 2711extern uintptr_t _Unwind_GetCFA(_Unwind_Context *); 2712 2713typedef struct _zend_jit_unwind_arg { 2714 int cnt; 2715 uintptr_t cfa[3]; 2716} zend_jit_unwind_arg; 2717 2718static int zend_jit_unwind_cb(_Unwind_Context *ctx, void *a) 2719{ 2720 zend_jit_unwind_arg *arg = (zend_jit_unwind_arg*)a; 2721 arg->cfa[arg->cnt] = _Unwind_GetCFA(ctx); 2722 arg->cnt++; 2723 if (arg->cnt == 3) { 2724 return 5; // _URC_END_OF_STACK 2725 } 2726 return 0; // _URC_NO_REASON; 2727} 2728 2729static void ZEND_FASTCALL zend_jit_touch_vm_stack_data(void *vm_stack_data) 2730{ 2731 zend_jit_unwind_arg arg; 2732 2733 memset(&arg, 0, sizeof(arg)); 2734 _Unwind_Backtrace(zend_jit_unwind_cb, &arg); 2735 if (arg.cnt == 3) { 2736 sp_adj[SP_ADJ_VM] = arg.cfa[2] - arg.cfa[1]; 2737 } 2738} 2739# else 2740static void ZEND_FASTCALL zend_jit_touch_vm_stack_data(void *vm_stack_data) 2741{ 2742 uintptr_t ret; 2743 2744 __asm__ ( 2745 "ldr %0, [x29]\n\t" 2746 "sub %0 ,%0, x29" 2747 : "=r"(ret)); 2748 2749 sp_adj[SP_ADJ_VM] = ret; 2750} 2751# endif 2752 2753extern void (ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data); 2754 2755static zend_never_inline void zend_jit_set_sp_adj_vm(void) 2756{ 2757 void (ZEND_FASTCALL *orig_zend_touch_vm_stack_data)(void *); 2758 2759 orig_zend_touch_vm_stack_data = zend_touch_vm_stack_data; 2760 zend_touch_vm_stack_data = zend_jit_touch_vm_stack_data; 2761 execute_ex(NULL); // set sp_adj[SP_ADJ_VM] 2762 zend_touch_vm_stack_data = orig_zend_touch_vm_stack_data; 2763} 2764#endif 2765 2766static int zend_jit_setup(void) 2767{ 2768 allowed_opt_flags = 0; 2769 2770#if ZTS 2771 tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset(); 2772# if defined(__FreeBSD__) 2773 if (tsrm_ls_cache_tcb_offset == 0) { 2774 TLSDescriptor **where; 2775 2776 __asm__( 2777 "adrp %0, :tlsdesc:_tsrm_ls_cache\n" 2778 "add %0, %0, :tlsdesc_lo12:_tsrm_ls_cache\n" 2779 : "=r" (where)); 2780 /* See https://github.com/ARM-software/abi-aa/blob/2a70c42d62e9c3eb5887fa50b71257f20daca6f9/aaelf64/aaelf64.rst 2781 * section "Relocations for thread-local storage". 2782 * The first entry holds a pointer to the variable's TLS descriptor resolver function and the second entry holds 2783 * a platform-specific offset or pointer. */ 2784 TLSDescriptor *tlsdesc = where[1]; 2785 2786 tsrm_tls_offset = tlsdesc->offset; 2787 /* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/22ca6db50f4e6bd75a141f57cf953d8de6531a06/lib/libc/gen/tls.c#L88) */ 2788 tsrm_tls_index = (tlsdesc->index + 1) * 8; 2789 } 2790# else 2791 ZEND_ASSERT(tsrm_ls_cache_tcb_offset != 0); 2792# endif 2793#endif 2794 2795 memset(sp_adj, 0, sizeof(sp_adj)); 2796#ifdef HAVE_GDB 2797 sp_adj[SP_ADJ_RET] = 0; 2798 sp_adj[SP_ADJ_ASSIGN] = 32; 2799 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 2800 zend_jit_set_sp_adj_vm(); // set sp_adj[SP_ADJ_VM] 2801#ifndef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 2802 || sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM] + HYBRID_SPAD; // sub r4, HYBRID_SPAD 2803#else 2804 || sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_VM]; 2805#endif 2806 } else if (GCC_GLOBAL_REGS) { 2807 || sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + SPAD; // sub r4, SPAD 2808 } else { 2809 || sp_adj[SP_ADJ_JIT] = sp_adj[SP_ADJ_RET] + NR_SPAD; // sub r4, NR_SPAD 2810 } 2811#endif 2812 2813 return SUCCESS; 2814} 2815 2816static ZEND_ATTRIBUTE_UNUSED int zend_jit_trap(dasm_State **Dst) 2817{ 2818 | brk #0 2819 return 1; 2820} 2821 2822static int zend_jit_align_func(dasm_State **Dst) 2823{ 2824 reuse_ip = 0; 2825 delayed_call_chain = 0; 2826 last_valid_opline = NULL; 2827 use_last_vald_opline = 0; 2828 track_last_valid_opline = 0; 2829 jit_return_label = -1; 2830 |.align 16 2831 return 1; 2832} 2833 2834static int zend_jit_prologue(dasm_State **Dst) 2835{ 2836 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 2837 | SUB_HYBRID_SPAD 2838 } else if (GCC_GLOBAL_REGS) { 2839 | stp x29, x30, [sp, # -SPAD]! // stack alignment 2840 |// mov x29, sp 2841 } else { 2842 | stp x29, x30, [sp, # -NR_SPAD]! // stack alignment 2843 |// mov x29, sp 2844 | stp FP, RX, T2 // save FP and IP 2845 | mov FP, FCARG1x 2846 } 2847 return 1; 2848} 2849 2850static int zend_jit_label(dasm_State **Dst, unsigned int label) 2851{ 2852 |=>label: 2853 return 1; 2854} 2855 2856static int zend_jit_save_call_chain(dasm_State **Dst, uint32_t call_level) 2857{ 2858 | // call->prev_execute_data = EX(call); 2859 if (call_level == 1) { 2860 | str xzr, EX:RX->prev_execute_data 2861 } else { 2862 | ldr REG0, EX->call 2863 | str REG0, EX:RX->prev_execute_data 2864 } 2865 | // EX(call) = call; 2866 | str RX, EX->call 2867 2868 delayed_call_chain = 0; 2869 2870 return 1; 2871} 2872 2873static int zend_jit_set_ip(dasm_State **Dst, const zend_op *opline) 2874{ 2875 if (last_valid_opline == opline) { 2876 zend_jit_use_last_valid_opline(); 2877 } else if (GCC_GLOBAL_REGS && last_valid_opline) { 2878 zend_jit_use_last_valid_opline(); 2879 | LOAD_64BIT_VAL TMP1, (opline - last_valid_opline) * sizeof(zend_op) 2880 | ADD_IP TMP1, TMP2 2881 } else { 2882 | LOAD_IP_ADDR opline 2883 } 2884 zend_jit_set_last_valid_opline(opline); 2885 2886 return 1; 2887} 2888 2889static int zend_jit_set_ip_ex(dasm_State **Dst, const zend_op *opline, bool set_ip_reg) 2890{ 2891 return zend_jit_set_ip(Dst, opline); 2892} 2893 2894static int zend_jit_set_valid_ip(dasm_State **Dst, const zend_op *opline) 2895{ 2896 if (delayed_call_chain) { 2897 if (!zend_jit_save_call_chain(Dst, delayed_call_level)) { 2898 return 0; 2899 } 2900 } 2901 if (!zend_jit_set_ip(Dst, opline)) { 2902 return 0; 2903 } 2904 reuse_ip = 0; 2905 return 1; 2906} 2907 2908static int zend_jit_check_timeout(dasm_State **Dst, const zend_op *opline, const void *exit_addr) 2909{ 2910 | MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1 2911 if (exit_addr) { 2912 | cbnz TMP1w, &exit_addr 2913 } else if (last_valid_opline == opline) { 2914 || zend_jit_use_last_valid_opline(); 2915 | cbnz TMP1w, ->interrupt_handler 2916 } else { 2917 | cbnz TMP1w, >1 2918 |.cold_code 2919 |1: 2920 | LOAD_IP_ADDR opline 2921 | b ->interrupt_handler 2922 |.code 2923 } 2924 return 1; 2925} 2926 2927static int zend_jit_trace_end_loop(dasm_State **Dst, int loop_label, const void *timeout_exit_addr) 2928{ 2929 if (timeout_exit_addr) { 2930 | MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1 2931 | cbz TMP1w, =>loop_label 2932 | b &timeout_exit_addr 2933 } else { 2934 | b =>loop_label 2935 } 2936 return 1; 2937} 2938 2939static int zend_jit_check_exception(dasm_State **Dst) 2940{ 2941 | MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1 2942 | cbnz TMP2, ->exception_handler 2943 return 1; 2944} 2945 2946static int zend_jit_check_exception_undef_result(dasm_State **Dst, const zend_op *opline) 2947{ 2948 if (opline->result_type & (IS_TMP_VAR|IS_VAR)) { 2949 | MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1 2950 | cbnz TMP2, ->exception_handler_undef 2951 return 1; 2952 } 2953 return zend_jit_check_exception(Dst); 2954} 2955 2956static int zend_jit_trace_begin(dasm_State **Dst, uint32_t trace_num, zend_jit_trace_info *parent, uint32_t exit_num) 2957{ 2958 2959 current_trace_num = trace_num; 2960 2961 | // EG(jit_trace_num) = trace_num; 2962 | LOAD_32BIT_VAL TMP1w, trace_num 2963 | MEM_STORE_32_ZTS str, TMP1w, executor_globals, jit_trace_num, TMP2 2964 2965 return 1; 2966} 2967 2968static int zend_jit_trace_end(dasm_State **Dst, zend_jit_trace_info *t) 2969{ 2970 uint32_t i; 2971 const void *exit_addr; 2972 2973 /* Emit veneers table for exit points (B instruction for each exit number) */ 2974 |.cold_code 2975 for (i = 0; i < t->exit_count; i++) { 2976 exit_addr = zend_jit_trace_get_exit_addr(i); 2977 if (!exit_addr) { 2978 return 0; 2979 } 2980 | b &exit_addr 2981 } 2982 |=>1: // end of the code 2983 |.code 2984 return 1; 2985} 2986 2987static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size, const void *from_addr, const void *to_addr) 2988{ 2989 int ret = 0; 2990 uint8_t *p, *end; 2991 const void *veneer = NULL; 2992 ptrdiff_t delta; 2993 2994 if (jmp_table_size) { 2995 const void **jmp_slot = (const void **)((char*)code + ZEND_MM_ALIGNED_SIZE_EX(size, sizeof(void*))); 2996 2997 do { 2998 if (*jmp_slot == from_addr) { 2999 *jmp_slot = to_addr; 3000 ret++; 3001 } 3002 jmp_slot++; 3003 } while (--jmp_table_size); 3004 } 3005 3006 end = (uint8_t*)code; 3007 p = end + size; 3008 while (p > end) { 3009 uint32_t *ins_ptr; 3010 uint32_t ins; 3011 3012 p -= 4; 3013 ins_ptr = (uint32_t*)p; 3014 ins = *ins_ptr; 3015 if ((ins & 0xfc000000u) == 0x14000000u) { 3016 // B (imm26:0..25) 3017 delta = (uint32_t*)from_addr - ins_ptr; 3018 if (((ins ^ (uint32_t)delta) & 0x01ffffffu) == 0) { 3019 delta = (uint32_t*)to_addr - ins_ptr; 3020 if (((delta + 0x02000000) >> 26) != 0) { 3021 abort(); // branch target out of range 3022 } 3023 *ins_ptr = (ins & 0xfc000000u) | ((uint32_t)delta & 0x03ffffffu); 3024 ret++; 3025 if (!veneer) { 3026 veneer = p; 3027 } 3028 } 3029 } else if ((ins & 0xff000000u) == 0x54000000u || 3030 (ins & 0x7e000000u) == 0x34000000u) { 3031 // B.cond, CBZ, CBNZ (imm19:5..23) 3032 delta = (uint32_t*)from_addr - ins_ptr; 3033 if (((ins ^ ((uint32_t)delta << 5)) & 0x00ffffe0u) == 0) { 3034 delta = (uint32_t*)to_addr - ins_ptr; 3035 if (((delta + 0x40000) >> 19) != 0) { 3036 if (veneer) { 3037 delta = (uint32_t*)veneer - ins_ptr; 3038 if (((delta + 0x40000) >> 19) != 0) { 3039 abort(); // branch target out of range 3040 } 3041 } else { 3042 abort(); // branch target out of range 3043 } 3044 } 3045 *ins_ptr = (ins & 0xff00001fu) | (((uint32_t)delta & 0x7ffffu) << 5); 3046 ret++; 3047 } 3048 } else if ((ins & 0x7e000000u) == 0x36000000u) { 3049 // TBZ, TBNZ (imm14:5..18) 3050 delta = (uint32_t*)from_addr - ins_ptr; 3051 if (((ins ^ ((uint32_t)delta << 5)) & 0x0007ffe0u) == 0) { 3052 delta = (uint32_t*)to_addr - ins_ptr; 3053 if (((delta + 0x2000) >> 14) != 0) { 3054 if (veneer) { 3055 delta = (uint32_t*)veneer - ins_ptr; 3056 if (((delta + 0x2000) >> 14) != 0) { 3057 abort(); // branch target out of range 3058 } 3059 } else { 3060 abort(); // branch target out of range 3061 } 3062 } 3063 *ins_ptr = (ins & 0xfff8001fu) | (((uint32_t)delta & 0x3fffu) << 5); 3064 ret++; 3065 } 3066 } 3067 } 3068 3069 JIT_CACHE_FLUSH(code, (char*)code + size); 3070 3071#ifdef HAVE_VALGRIND 3072 VALGRIND_DISCARD_TRANSLATIONS(code, size); 3073#endif 3074 3075 return ret; 3076} 3077 3078static int zend_jit_link_side_trace(const void *code, size_t size, uint32_t jmp_table_size, uint32_t exit_num, const void *addr) 3079{ 3080 return zend_jit_patch(code, size, jmp_table_size, zend_jit_trace_get_exit_addr(exit_num), addr); 3081} 3082 3083static int zend_jit_trace_link_to_root(dasm_State **Dst, zend_jit_trace_info *t, const void *timeout_exit_addr) 3084{ 3085 const void *link_addr; 3086 size_t prologue_size; 3087 3088 /* Skip prologue. */ 3089 // TODO: don't hardcode this ??? 3090 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 3091#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 3092 prologue_size = 0; 3093#else 3094 // sub sp, sp, #0x20 3095 prologue_size = 4; 3096#endif 3097 } else if (GCC_GLOBAL_REGS) { 3098 // stp x29, x30, [sp, # -SPAD]! 3099 prologue_size = 4; 3100 } else { 3101 // stp x29, x30, [sp, # -NR_SPAD]! // stack alignment 3102 // stp FP, RX, T2 3103 // mov FP, FCARG1x 3104 prologue_size = 12; 3105 } 3106 link_addr = (const void*)((const char*)t->code_start + prologue_size); 3107 3108 if (timeout_exit_addr) { 3109 /* Check timeout for links to LOOP */ 3110 | MEM_LOAD_8_ZTS ldrb, TMP1w, executor_globals, vm_interrupt, TMP1 3111 | cbz TMP1w, &link_addr 3112 | b &timeout_exit_addr 3113 } else { 3114 | b &link_addr 3115 } 3116 return 1; 3117} 3118 3119static int zend_jit_trace_return(dasm_State **Dst, bool original_handler, const zend_op *opline) 3120{ 3121 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 3122 | ADD_HYBRID_SPAD 3123 if (!original_handler) { 3124 | JMP_IP TMP1 3125 } else { 3126 | ldr REG0, EX->func 3127 | ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 3128 | ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)] 3129 | ldr REG0, [IP, REG0] 3130 | br REG0 3131 } 3132 } else if (GCC_GLOBAL_REGS) { 3133 | ldp x29, x30, [sp], # SPAD // stack alignment 3134 if (!original_handler) { 3135 | JMP_IP TMP1 3136 } else { 3137 | ldr REG0, EX->func 3138 | ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 3139 | ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)] 3140 | ldr REG0, [IP, REG0] 3141 | br REG0 3142 } 3143 } else { 3144 if (original_handler) { 3145 | mov FCARG1x, FP 3146 | ldr REG0, EX->func 3147 | ldr REG0, [REG0, #offsetof(zend_op_array, reserved[zend_func_info_rid])] 3148 | ldr REG0, [REG0, #offsetof(zend_jit_op_array_trace_extension, offset)] 3149 | ldr REG0, [IP, REG0] 3150 | blr REG0 3151 } 3152 | ldp FP, RX, T2 // restore FP and IP 3153 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 3154 if (!original_handler || !opline || 3155 (opline->opcode != ZEND_RETURN 3156 && opline->opcode != ZEND_RETURN_BY_REF 3157 && opline->opcode != ZEND_GENERATOR_RETURN 3158 && opline->opcode != ZEND_GENERATOR_CREATE 3159 && opline->opcode != ZEND_YIELD 3160 && opline->opcode != ZEND_YIELD_FROM)) { 3161 | mov RETVALx, #2 // ZEND_VM_LEAVE 3162 } 3163 | ret 3164 } 3165 return 1; 3166} 3167 3168static int zend_jit_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint8_t type) 3169{ 3170 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 3171 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 3172 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); 3173 3174 if (!exit_addr) { 3175 return 0; 3176 } 3177 3178 | IF_NOT_ZVAL_TYPE var_addr, type, &exit_addr, ZREG_TMP1 3179 3180 return 1; 3181} 3182 3183static int zend_jit_scalar_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t var) 3184{ 3185 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 3186 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 3187 3188 if (!exit_addr) { 3189 return 0; 3190 } 3191 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP1w, FP, var+offsetof(zval, u1.v.type), TMP1 3192 | cmp TMP1w, #IS_STRING 3193 | bhs &exit_addr 3194 3195 return 1; 3196} 3197static int zend_jit_packed_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint32_t op_info) 3198{ 3199 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD); 3200 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 3201 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); 3202 3203 if (!exit_addr) { 3204 return 0; 3205 } 3206 3207 | GET_ZVAL_LVAL ZREG_FCARG1, var_addr, TMP1 3208 if (op_info & MAY_BE_ARRAY_PACKED) { 3209 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 3210 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 3211 | beq &exit_addr 3212 } else { 3213 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 3214 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 3215 | bne &exit_addr 3216 } 3217 3218 return 1; 3219} 3220 3221static 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) 3222{ 3223 zend_jit_op_array_trace_extension *jit_extension = 3224 (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array); 3225 size_t offset = jit_extension->offset; 3226 const void *handler = 3227 (zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler; 3228 3229 if (!zend_jit_set_valid_ip(Dst, opline)) { 3230 return 0; 3231 } 3232 if (!GCC_GLOBAL_REGS) { 3233 | mov FCARG1x, FP 3234 } 3235 | EXT_CALL handler, REG0 3236 if (may_throw 3237 && opline->opcode != ZEND_RETURN 3238 && opline->opcode != ZEND_RETURN_BY_REF) { 3239 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 3240 | cbnz REG0, ->exception_handler 3241 } 3242 3243 while (trace->op != ZEND_JIT_TRACE_VM && trace->op != ZEND_JIT_TRACE_END) { 3244 trace++; 3245 } 3246 3247 if (!GCC_GLOBAL_REGS 3248 && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_RETURN)) { 3249 if (opline->opcode == ZEND_RETURN || 3250 opline->opcode == ZEND_RETURN_BY_REF || 3251 opline->opcode == ZEND_DO_UCALL || 3252 opline->opcode == ZEND_DO_FCALL_BY_NAME || 3253 opline->opcode == ZEND_DO_FCALL || 3254 opline->opcode == ZEND_GENERATOR_CREATE) { 3255 | MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, TMP1 3256 } 3257 } 3258 3259 if (zend_jit_trace_may_exit(op_array, opline)) { 3260 if (opline->opcode == ZEND_RETURN || 3261 opline->opcode == ZEND_RETURN_BY_REF || 3262 opline->opcode == ZEND_GENERATOR_CREATE) { 3263 3264 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 3265#if 0 3266 /* this check should be handled by the following OPLINE guard or jmp [IP] */ 3267 | LOAD_ADDR TMP1, zend_jit_halt_op 3268 | cmp IP, TMP1 3269 | beq ->trace_halt 3270#endif 3271 } else if (GCC_GLOBAL_REGS) { 3272 | cbz IP, ->trace_halt 3273 } else { 3274 | tst RETVALw, RETVALw 3275 | blt ->trace_halt 3276 } 3277 } else if (opline->opcode == ZEND_EXIT || 3278 opline->opcode == ZEND_GENERATOR_RETURN || 3279 opline->opcode == ZEND_YIELD || 3280 opline->opcode == ZEND_YIELD_FROM) { 3281 | b ->trace_halt 3282 } 3283 if (trace->op != ZEND_JIT_TRACE_END || 3284 (trace->stop != ZEND_JIT_TRACE_STOP_RETURN && 3285 trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) { 3286 3287 const zend_op *next_opline = trace->opline; 3288 const zend_op *exit_opline = NULL; 3289 uint32_t exit_point; 3290 const void *exit_addr; 3291 uint32_t old_info = 0; 3292 uint32_t old_res_info = 0; 3293 zend_jit_trace_stack *stack = JIT_G(current_frame)->stack; 3294 3295 if (zend_is_smart_branch(opline)) { 3296 bool exit_if_true = 0; 3297 exit_opline = zend_jit_trace_get_exit_opline(trace, opline + 1, &exit_if_true); 3298 } else { 3299 switch (opline->opcode) { 3300 case ZEND_JMPZ: 3301 case ZEND_JMPNZ: 3302 case ZEND_JMPZ_EX: 3303 case ZEND_JMPNZ_EX: 3304 case ZEND_JMP_SET: 3305 case ZEND_COALESCE: 3306 case ZEND_JMP_NULL: 3307 case ZEND_FE_RESET_R: 3308 case ZEND_FE_RESET_RW: 3309 exit_opline = (trace->opline == opline + 1) ? 3310 OP_JMP_ADDR(opline, opline->op2) : 3311 opline + 1; 3312 break; 3313 case ZEND_FE_FETCH_R: 3314 case ZEND_FE_FETCH_RW: 3315 exit_opline = (trace->opline == opline + 1) ? 3316 ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) : 3317 opline + 1; 3318 break; 3319 3320 } 3321 } 3322 3323 switch (opline->opcode) { 3324 case ZEND_FE_FETCH_R: 3325 case ZEND_FE_FETCH_RW: 3326 if (opline->op2_type != IS_UNUSED) { 3327 old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var)); 3328 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1); 3329 } 3330 break; 3331 } 3332 3333 if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) { 3334 old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); 3335 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1); 3336 } 3337 exit_point = zend_jit_trace_get_exit_point(exit_opline, 0); 3338 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 3339 3340 if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) { 3341 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info); 3342 } 3343 switch (opline->opcode) { 3344 case ZEND_FE_FETCH_R: 3345 case ZEND_FE_FETCH_RW: 3346 if (opline->op2_type != IS_UNUSED) { 3347 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var), old_info); 3348 } 3349 break; 3350 } 3351 3352 if (!exit_addr) { 3353 return 0; 3354 } 3355 | CMP_IP next_opline, TMP1, TMP2 3356 | bne &exit_addr 3357 } 3358 } 3359 3360 zend_jit_set_last_valid_opline(trace->opline); 3361 3362 return 1; 3363} 3364 3365static int zend_jit_handler(dasm_State **Dst, const zend_op *opline, int may_throw) 3366{ 3367 const void *handler; 3368 3369 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 3370 handler = zend_get_opcode_handler_func(opline); 3371 } else { 3372 handler = opline->handler; 3373 } 3374 3375 if (!zend_jit_set_valid_ip(Dst, opline)) { 3376 return 0; 3377 } 3378 if (!GCC_GLOBAL_REGS) { 3379 | mov FCARG1x, FP 3380 } 3381 | EXT_CALL handler, REG0 3382 if (may_throw) { 3383 zend_jit_check_exception(Dst); 3384 } 3385 3386 /* Skip the following OP_DATA */ 3387 switch (opline->opcode) { 3388 case ZEND_ASSIGN_DIM: 3389 case ZEND_ASSIGN_OBJ: 3390 case ZEND_ASSIGN_STATIC_PROP: 3391 case ZEND_ASSIGN_DIM_OP: 3392 case ZEND_ASSIGN_OBJ_OP: 3393 case ZEND_ASSIGN_STATIC_PROP_OP: 3394 case ZEND_ASSIGN_STATIC_PROP_REF: 3395 case ZEND_ASSIGN_OBJ_REF: 3396 zend_jit_set_last_valid_opline(opline + 2); 3397 break; 3398 default: 3399 zend_jit_set_last_valid_opline(opline + 1); 3400 break; 3401 } 3402 3403 return 1; 3404} 3405 3406static int zend_jit_tail_handler(dasm_State **Dst, const zend_op *opline) 3407{ 3408 if (!zend_jit_set_valid_ip(Dst, opline)) { 3409 return 0; 3410 } 3411 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 3412 if (opline->opcode == ZEND_DO_UCALL || 3413 opline->opcode == ZEND_DO_FCALL_BY_NAME || 3414 opline->opcode == ZEND_DO_FCALL || 3415 opline->opcode == ZEND_RETURN) { 3416 3417 /* Use inlined HYBRID VM handler */ 3418 const void *handler = opline->handler; 3419 3420 | ADD_HYBRID_SPAD 3421 | EXT_JMP handler, REG0 3422 } else { 3423 const void *handler = zend_get_opcode_handler_func(opline); 3424 3425 | EXT_CALL handler, REG0 3426 | ADD_HYBRID_SPAD 3427 | JMP_IP TMP1 3428 } 3429 } else { 3430 const void *handler = opline->handler; 3431 3432 if (GCC_GLOBAL_REGS) { 3433 | ldp x29, x30, [sp], # SPAD // stack alignment 3434 } else { 3435 | mov FCARG1x, FP 3436 | ldp FP, RX, T2 // restore FP and IP 3437 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 3438 } 3439 | EXT_JMP handler, REG0 3440 } 3441 zend_jit_reset_last_valid_opline(); 3442 return 1; 3443} 3444 3445static int zend_jit_trace_opline_guard(dasm_State **Dst, const zend_op *opline) 3446{ 3447 uint32_t exit_point = zend_jit_trace_get_exit_point(NULL, 0); 3448 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 3449 3450 if (!exit_addr) { 3451 return 0; 3452 } 3453 | CMP_IP opline, TMP1, TMP2 3454 | bne &exit_addr 3455 3456 zend_jit_set_last_valid_opline(opline); 3457 3458 return 1; 3459} 3460 3461static int zend_jit_jmp(dasm_State **Dst, unsigned int target_label) 3462{ 3463 | b =>target_label 3464 return 1; 3465} 3466 3467static int zend_jit_cond_jmp(dasm_State **Dst, const zend_op *next_opline, unsigned int target_label) 3468{ 3469 | CMP_IP next_opline, TMP1, TMP2 3470 | bne =>target_label 3471 3472 zend_jit_set_last_valid_opline(next_opline); 3473 3474 return 1; 3475} 3476 3477#ifdef CONTEXT_THREADED_JIT 3478static int zend_jit_context_threaded_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block) 3479{ 3480 | NIY // TODO 3481 return 1; 3482} 3483#endif 3484 3485static int zend_jit_call(dasm_State **Dst, const zend_op *opline, unsigned int next_block) 3486{ 3487#ifdef CONTEXT_THREADED_JIT 3488 return zend_jit_context_threaded_call(Dst, opline, next_block); 3489#else 3490 return zend_jit_tail_handler(Dst, opline); 3491#endif 3492} 3493 3494static int zend_jit_spill_store(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info, bool set_type) 3495{ 3496 ZEND_ASSERT(Z_MODE(src) == IS_REG); 3497 ZEND_ASSERT(Z_MODE(dst) == IS_MEM_ZVAL); 3498 3499 if ((info & MAY_BE_ANY) == MAY_BE_LONG) { 3500 | SET_ZVAL_LVAL_FROM_REG dst, Rx(Z_REG(src)), TMP1 3501 if (set_type && 3502 (Z_REG(dst) != ZREG_FP || 3503 !JIT_G(current_frame) || 3504 STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_LONG)) { 3505 | SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2 3506 } 3507 } else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) { 3508 | SET_ZVAL_DVAL dst, Z_REG(src), ZREG_TMP1 3509 if (set_type && 3510 (Z_REG(dst) != ZREG_FP || 3511 !JIT_G(current_frame) || 3512 STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(dst))) != IS_DOUBLE)) { 3513 | SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2 3514 } 3515 } else { 3516 ZEND_UNREACHABLE(); 3517 } 3518 return 1; 3519} 3520 3521static int zend_jit_load_reg(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info) 3522{ 3523 ZEND_ASSERT(Z_MODE(src) == IS_MEM_ZVAL); 3524 ZEND_ASSERT(Z_MODE(dst) == IS_REG); 3525 3526 if ((info & MAY_BE_ANY) == MAY_BE_LONG) { 3527 | GET_ZVAL_LVAL Z_REG(dst), src, TMP1 3528 } else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) { 3529 | GET_ZVAL_DVAL Z_REG(dst), src, ZREG_TMP1 3530 } else { 3531 ZEND_UNREACHABLE(); 3532 } 3533 return 1; 3534} 3535 3536static int zend_jit_store_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg, bool set_type) 3537{ 3538 zend_jit_addr src = ZEND_ADDR_REG(reg); 3539 zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var)); 3540 3541 return zend_jit_spill_store(Dst, src, dst, info, set_type); 3542} 3543 3544static int zend_jit_store_var_type(dasm_State **Dst, int var, uint32_t type) 3545{ 3546 zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var)); 3547 3548 | SET_ZVAL_TYPE_INFO dst, type, TMP1w, TMP2 3549 return 1; 3550} 3551 3552static int zend_jit_store_var_if_necessary(dasm_State **Dst, int var, zend_jit_addr src, uint32_t info) 3553{ 3554 if (Z_MODE(src) == IS_REG && Z_STORE(src)) { 3555 zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); 3556 return zend_jit_spill_store(Dst, src, dst, info, 1); 3557 } 3558 return 1; 3559} 3560 3561static 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) 3562{ 3563 if (Z_MODE(src) == IS_REG && Z_STORE(src)) { 3564 zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); 3565 bool set_type = 1; 3566 3567 if ((info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == 3568 (old_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF))) { 3569 if (Z_MODE(old) != IS_REG || Z_LOAD(old) || Z_STORE(old)) { 3570 set_type = 0; 3571 } 3572 } 3573 return zend_jit_spill_store(Dst, src, dst, info, set_type); 3574 } 3575 return 1; 3576} 3577 3578static int zend_jit_load_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg) 3579{ 3580 zend_jit_addr src = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var)); 3581 zend_jit_addr dst = ZEND_ADDR_REG(reg); 3582 3583 return zend_jit_load_reg(Dst, src, dst, info); 3584} 3585 3586static int zend_jit_invalidate_var_if_necessary(dasm_State **Dst, zend_uchar op_type, zend_jit_addr addr, znode_op op) 3587{ 3588 if ((op_type & (IS_TMP_VAR|IS_VAR)) && Z_MODE(addr) == IS_REG && !Z_LOAD(addr) && !Z_STORE(addr)) { 3589 zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var); 3590 | SET_ZVAL_TYPE_INFO dst, IS_UNDEF, TMP1w, TMP2 3591 } 3592 return 1; 3593} 3594 3595static int zend_jit_update_regs(dasm_State **Dst, uint32_t var, zend_jit_addr src, zend_jit_addr dst, uint32_t info) 3596{ 3597 if (!zend_jit_same_addr(src, dst)) { 3598 if (Z_MODE(src) == IS_REG) { 3599 if (Z_MODE(dst) == IS_REG) { 3600 if ((info & MAY_BE_ANY) == MAY_BE_LONG) { 3601 | mov Rx(Z_REG(dst)), Rx(Z_REG(src)) 3602 } else if ((info & MAY_BE_ANY) == MAY_BE_DOUBLE) { 3603 | fmov Rd(Z_REG(dst)-ZREG_V0), Rd(Z_REG(src)-ZREG_V0) 3604 } else { 3605 ZEND_UNREACHABLE(); 3606 } 3607 if (!Z_LOAD(src) && !Z_STORE(src) && Z_STORE(dst)) { 3608 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); 3609 3610 if (!zend_jit_spill_store(Dst, dst, var_addr, info, 3611 JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 3612 JIT_G(current_frame) == NULL || 3613 STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN || 3614 (1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY) 3615 )) { 3616 return 0; 3617 } 3618 } 3619 } else if (Z_MODE(dst) == IS_MEM_ZVAL) { 3620 if (!Z_LOAD(src) && !Z_STORE(src)) { 3621 if (!zend_jit_spill_store(Dst, src, dst, info, 3622 JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 3623 JIT_G(current_frame) == NULL || 3624 STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN || 3625 (1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY) 3626 )) { 3627 return 0; 3628 } 3629 } 3630 } else { 3631 ZEND_UNREACHABLE(); 3632 } 3633 } else if (Z_MODE(src) == IS_MEM_ZVAL) { 3634 if (Z_MODE(dst) == IS_REG) { 3635 if (!zend_jit_load_reg(Dst, src, dst, info)) { 3636 return 0; 3637 } 3638 } else { 3639 ZEND_UNREACHABLE(); 3640 } 3641 } else { 3642 ZEND_UNREACHABLE(); 3643 } 3644 } else if (Z_MODE(dst) == IS_REG && Z_STORE(dst)) { 3645 dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); 3646 if (!zend_jit_spill_store(Dst, src, dst, info, 3647 JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 3648 JIT_G(current_frame) == NULL || 3649 STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var)) == IS_UNKNOWN || 3650 (1 << STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(var))) != (info & MAY_BE_ANY) 3651 )) { 3652 return 0; 3653 } 3654 } 3655 return 1; 3656} 3657 3658static int zend_jit_escape_if_undef_r0(dasm_State **Dst, int var, uint32_t flags, const zend_op *opline) 3659{ 3660 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 3661 3662 | IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1 3663 3664 if (flags & ZEND_JIT_EXIT_RESTORE_CALL) { 3665 if (!zend_jit_save_call_chain(Dst, -1)) { 3666 return 0; 3667 } 3668 } 3669 3670 ZEND_ASSERT(opline); 3671 3672 if ((opline-1)->opcode != ZEND_FETCH_CONSTANT 3673 && (opline-1)->opcode != ZEND_FETCH_LIST_R 3674 && ((opline-1)->op1_type & (IS_VAR|IS_TMP_VAR)) 3675 && !(flags & ZEND_JIT_EXIT_FREE_OP1)) { 3676 val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline-1)->op1.var); 3677 3678 | IF_NOT_ZVAL_REFCOUNTED val_addr, >2, ZREG_TMP1, ZREG_TMP2 3679 | GET_ZVAL_PTR TMP1, val_addr, TMP2 3680 | GC_ADDREF TMP1, TMP2w 3681 |2: 3682 } 3683 3684 | LOAD_IP_ADDR (opline - 1) 3685 | b ->trace_escape 3686 |1: 3687 3688 return 1; 3689} 3690 3691static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg) 3692{ 3693 zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var)); 3694 3695 if (reg == ZREG_LONG_MIN_MINUS_1) { 3696 uint64_t val = 0xc3e0000000000000; 3697 | SET_ZVAL_LVAL dst, val, TMP1, TMP2 3698 | SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2 3699 } else if (reg == ZREG_LONG_MIN) { 3700 uint64_t val = 0x8000000000000000; 3701 | SET_ZVAL_LVAL dst, val, TMP1, TMP2 3702 | SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2 3703 } else if (reg == ZREG_LONG_MAX) { 3704 uint64_t val = 0x7fffffffffffffff; 3705 | SET_ZVAL_LVAL dst, val, TMP1, TMP2 3706 | SET_ZVAL_TYPE_INFO dst, IS_LONG, TMP1w, TMP2 3707 } else if (reg == ZREG_LONG_MAX_PLUS_1) { 3708 uint64_t val = 0x43e0000000000000; 3709 | SET_ZVAL_LVAL dst, val, TMP1, TMP2 3710 | SET_ZVAL_TYPE_INFO dst, IS_DOUBLE, TMP1w, TMP2 3711 } else if (reg == ZREG_NULL) { 3712 | SET_ZVAL_TYPE_INFO dst, IS_NULL, TMP1w, TMP2 3713 } else if (reg == ZREG_ZVAL_TRY_ADDREF) { 3714 | IF_NOT_ZVAL_REFCOUNTED dst, >1, ZREG_TMP1, ZREG_TMP2 3715 | GET_ZVAL_PTR TMP1, dst, TMP2 3716 | GC_ADDREF TMP1, TMP2w 3717 |1: 3718 } else if (reg == ZREG_ZVAL_COPY_GPR0) { 3719 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 3720 3721 | ZVAL_COPY_VALUE dst, -1, val_addr, -1, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3722 | TRY_ADDREF -1, REG1w, REG2, TMP1w 3723 } else { 3724 ZEND_UNREACHABLE(); 3725 } 3726 return 1; 3727} 3728 3729static int zend_jit_free_trampoline(dasm_State **Dst) 3730{ 3731 | // if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) 3732 | ldr TMP1w, [REG0, #offsetof(zend_function, common.fn_flags)] 3733 | TST_32_WITH_CONST TMP1w, ZEND_ACC_CALL_VIA_TRAMPOLINE, TMP2w 3734 | beq >1 3735 | mov FCARG1x, REG0 3736 | EXT_CALL zend_jit_free_trampoline_helper, REG0 3737 |1: 3738 return 1; 3739} 3740 3741static 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) 3742{ 3743 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_LONG)) { 3744 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2, ZREG_TMP1 3745 } 3746 if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) { 3747 | ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3748 } 3749 if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, MAY_BE_LONG)) { 3750 return 0; 3751 } 3752 if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { 3753 | LONG_ADD_SUB_WITH_IMM adds, op1_def_addr, Z_L(1), TMP1, TMP2 3754 } else { 3755 | LONG_ADD_SUB_WITH_IMM subs, op1_def_addr, Z_L(1), TMP1, TMP2 3756 } 3757 3758 if (may_overflow && 3759 (((op1_def_info & MAY_BE_GUARD) && (op1_def_info & MAY_BE_LONG)) || 3760 ((opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD) && (res_info & MAY_BE_LONG))))) { 3761 int32_t exit_point; 3762 const void *exit_addr; 3763 zend_jit_trace_stack *stack; 3764 uint32_t old_op1_info, old_res_info = 0; 3765 3766 stack = JIT_G(current_frame)->stack; 3767 old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var)); 3768 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_DOUBLE, 0); 3769 if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { 3770 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MAX_PLUS_1); 3771 } else { 3772 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MIN_MINUS_1); 3773 } 3774 if (opline->result_type != IS_UNUSED) { 3775 old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); 3776 if (opline->opcode == ZEND_PRE_INC) { 3777 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0); 3778 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX_PLUS_1); 3779 } else if (opline->opcode == ZEND_PRE_DEC) { 3780 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0); 3781 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN_MINUS_1); 3782 } else if (opline->opcode == ZEND_POST_INC) { 3783 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0); 3784 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX); 3785 } else if (opline->opcode == ZEND_POST_DEC) { 3786 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0); 3787 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN); 3788 } 3789 } 3790 3791 exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); 3792 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 3793 if (!exit_addr) { 3794 return 0; 3795 } 3796 | bvs &exit_addr 3797 3798 if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && 3799 opline->result_type != IS_UNUSED) { 3800 | ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3801 } 3802 3803 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info); 3804 if (opline->result_type != IS_UNUSED) { 3805 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info); 3806 } 3807 } else if (may_overflow) { 3808 | bvs >1 3809 if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && 3810 opline->result_type != IS_UNUSED) { 3811 | ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3812 } 3813 |.cold_code 3814 |1: 3815 if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { 3816 uint64_t val = 0x43e0000000000000; 3817 if (Z_MODE(op1_def_addr) == IS_REG) { 3818 | LOAD_64BIT_VAL TMP1, val 3819 | fmov Rd(Z_REG(op1_def_addr)-ZREG_V0), TMP1 3820 } else { 3821 | SET_ZVAL_LVAL op1_def_addr, val, TMP2, TMP1 3822 } 3823 } else { 3824 uint64_t val = 0xc3e0000000000000; 3825 if (Z_MODE(op1_def_addr) == IS_REG) { 3826 | LOAD_64BIT_VAL TMP1, val 3827 | fmov Rd(Z_REG(op1_def_addr)-ZREG_V0), TMP1 3828 } else { 3829 | SET_ZVAL_LVAL op1_def_addr, val, TMP2, TMP1 3830 } 3831 } 3832 if (Z_MODE(op1_def_addr) == IS_MEM_ZVAL) { 3833 | SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE, TMP1w, TMP2 3834 } 3835 if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && 3836 opline->result_type != IS_UNUSED) { 3837 | ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_DOUBLE, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3838 } 3839 | b >3 3840 |.code 3841 } else { 3842 if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && 3843 opline->result_type != IS_UNUSED) { 3844 | ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3845 } 3846 } 3847 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) { 3848 |.cold_code 3849 |2: 3850 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) { 3851 | SET_EX_OPLINE opline, REG0 3852 if (op1_info & MAY_BE_UNDEF) { 3853 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >2, ZREG_TMP1 3854 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 3855 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 3856 | EXT_CALL zend_jit_undefined_op_helper, REG0 3857 | SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2 3858 op1_info |= MAY_BE_NULL; 3859 } 3860 |2: 3861 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 3862 3863 | // ZVAL_DEREF(var_ptr); 3864 if (op1_info & MAY_BE_REF) { 3865 | IF_NOT_Z_TYPE, FCARG1x, IS_REFERENCE, >2, TMP1w 3866 | GET_Z_PTR FCARG1x, FCARG1x 3867 | ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)] 3868 | cbz TMP1, >1 3869 if (RETURN_VALUE_USED(opline)) { 3870 | LOAD_ZVAL_ADDR FCARG2x, res_addr 3871 } else { 3872 | mov FCARG2x, xzr 3873 } 3874 if (opline->opcode == ZEND_PRE_INC) { 3875 | EXT_CALL zend_jit_pre_inc_typed_ref, REG0 3876 } else if (opline->opcode == ZEND_PRE_DEC) { 3877 | EXT_CALL zend_jit_pre_dec_typed_ref, REG0 3878 } else if (opline->opcode == ZEND_POST_INC) { 3879 | EXT_CALL zend_jit_post_inc_typed_ref, REG0 3880 } else if (opline->opcode == ZEND_POST_DEC) { 3881 | EXT_CALL zend_jit_post_dec_typed_ref, REG0 3882 } else { 3883 ZEND_UNREACHABLE(); 3884 } 3885 zend_jit_check_exception(Dst); 3886 | b >3 3887 |1: 3888 | add FCARG1x, FCARG1x, #offsetof(zend_reference, val) 3889 |2: 3890 } 3891 3892 if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) { 3893 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 3894 3895 | ZVAL_COPY_VALUE res_addr, res_use_info, val_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3896 | TRY_ADDREF op1_info, REG0w, REG2, TMP1w 3897 } 3898 if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { 3899 if (opline->opcode == ZEND_PRE_INC && opline->result_type != IS_UNUSED) { 3900 | LOAD_ZVAL_ADDR FCARG2x, res_addr 3901 | EXT_CALL zend_jit_pre_inc, REG0 3902 } else { 3903 | EXT_CALL increment_function, REG0 3904 } 3905 } else { 3906 if (opline->opcode == ZEND_PRE_DEC && opline->result_type != IS_UNUSED) { 3907 | LOAD_ZVAL_ADDR FCARG2x, res_addr 3908 | EXT_CALL zend_jit_pre_dec, REG0 3909 } else { 3910 | EXT_CALL decrement_function, REG0 3911 } 3912 } 3913 if (may_throw) { 3914 zend_jit_check_exception(Dst); 3915 } 3916 } else { 3917 zend_reg tmp_reg; 3918 3919 if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) { 3920 | ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, MAY_BE_DOUBLE, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3921 } 3922 if (Z_MODE(op1_def_addr) == IS_REG) { 3923 tmp_reg = Z_REG(op1_def_addr); 3924 } else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) { 3925 tmp_reg = Z_REG(op1_addr); 3926 } else { 3927 tmp_reg = ZREG_FPR0; 3928 } 3929 | GET_ZVAL_DVAL tmp_reg, op1_addr, ZREG_TMP1 3930 if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { 3931 uint64_t val = 0x3ff0000000000000; // 1.0 3932 | LOAD_64BIT_VAL TMP1, val 3933 | fmov FPTMP, TMP1 3934 | fadd Rd(tmp_reg-ZREG_V0), Rd(tmp_reg-ZREG_V0), FPTMP 3935 } else { 3936 uint64_t val = 0x3ff0000000000000; // 1.0 3937 | LOAD_64BIT_VAL TMP1, val 3938 | fmov FPTMP, TMP1 3939 | fsub Rd(tmp_reg-ZREG_V0), Rd(tmp_reg-ZREG_V0), FPTMP 3940 } 3941 | SET_ZVAL_DVAL op1_def_addr, tmp_reg, ZREG_TMP1 3942 if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && 3943 opline->result_type != IS_UNUSED) { 3944 | ZVAL_COPY_VALUE res_addr, res_use_info, op1_addr, op1_def_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 3945 | TRY_ADDREF op1_def_info, REG0w, REG1, TMP1w 3946 } 3947 } 3948 | b >3 3949 |.code 3950 } 3951 |3: 3952 if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_def_addr, op1_def_info, op1_addr, op1_info)) { 3953 return 0; 3954 } 3955 if (opline->result_type != IS_UNUSED) { 3956 if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) { 3957 return 0; 3958 } 3959 } 3960 return 1; 3961} 3962 3963static int zend_jit_opline_uses_reg(const zend_op *opline, int8_t reg) 3964{ 3965 if ((opline+1)->opcode == ZEND_OP_DATA 3966 && ((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV)) 3967 && JIT_G(current_frame)->stack[EX_VAR_TO_NUM((opline+1)->op1.var)].reg == reg) { 3968 return 1; 3969 } 3970 return 3971 ((opline->result_type & (IS_VAR|IS_TMP_VAR|IS_CV)) && 3972 JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->result.var)].reg == reg) || 3973 ((opline->op1_type & (IS_VAR|IS_TMP_VAR|IS_CV)) && 3974 JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op1.var)].reg == reg) || 3975 ((opline->op2_type & (IS_VAR|IS_TMP_VAR|IS_CV)) && 3976 JIT_G(current_frame)->stack[EX_VAR_TO_NUM(opline->op2.var)].reg == reg); 3977} 3978 3979static int zend_jit_math_long_long(dasm_State **Dst, 3980 const zend_op *opline, 3981 zend_uchar opcode, 3982 zend_jit_addr op1_addr, 3983 zend_jit_addr op2_addr, 3984 zend_jit_addr res_addr, 3985 uint32_t res_info, 3986 uint32_t res_use_info, 3987 int may_overflow) 3988{ 3989 bool same_ops = zend_jit_same_addr(op1_addr, op2_addr); 3990 zend_reg result_reg; 3991 zend_reg tmp_reg = ZREG_REG0; 3992 bool use_ovf_flag = 1; 3993 3994 if (Z_MODE(res_addr) == IS_REG && (res_info & MAY_BE_LONG)) { 3995 if (may_overflow && (res_info & MAY_BE_GUARD) 3996 && JIT_G(current_frame) 3997 && zend_jit_opline_uses_reg(opline, Z_REG(res_addr))) { 3998 result_reg = ZREG_REG0; 3999 } else { 4000 result_reg = Z_REG(res_addr); 4001 } 4002 } else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr) && !may_overflow) { 4003 result_reg = Z_REG(op1_addr); 4004 } else if (Z_REG(res_addr) != ZREG_REG0) { 4005 result_reg = ZREG_REG0; 4006 } else { 4007 /* ASSIGN_DIM_OP */ 4008 result_reg = ZREG_FCARG1; 4009 tmp_reg = ZREG_FCARG1; 4010 } 4011 4012 if (opcode == ZEND_MUL && 4013 Z_MODE(op2_addr) == IS_CONST_ZVAL && 4014 Z_LVAL_P(Z_ZV(op2_addr)) == 2) { 4015 if (Z_MODE(op1_addr) == IS_REG) { 4016 | adds Rx(result_reg), Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr)) 4017 } else { 4018 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4019 | adds Rx(result_reg), Rx(result_reg), Rx(result_reg) 4020 } 4021 } else if (opcode == ZEND_MUL && 4022 Z_MODE(op2_addr) == IS_CONST_ZVAL && 4023 !may_overflow && 4024 zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr)))) { 4025 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4026 | mov TMP1, #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr))) 4027 | lsl Rx(result_reg), Rx(result_reg), TMP1 4028 } else if (opcode == ZEND_MUL && 4029 Z_MODE(op1_addr) == IS_CONST_ZVAL && 4030 Z_LVAL_P(Z_ZV(op1_addr)) == 2) { 4031 if (Z_MODE(op2_addr) == IS_REG) { 4032 | adds Rx(result_reg), Rx(Z_REG(op2_addr)), Rx(Z_REG(op2_addr)) 4033 } else { 4034 | GET_ZVAL_LVAL result_reg, op2_addr, TMP1 4035 | adds Rx(result_reg), Rx(result_reg), Rx(result_reg) 4036 } 4037 } else if (opcode == ZEND_MUL && 4038 Z_MODE(op1_addr) == IS_CONST_ZVAL && 4039 !may_overflow && 4040 zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op1_addr)))) { 4041 | GET_ZVAL_LVAL result_reg, op2_addr, TMP1 4042 | mov TMP1, #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op1_addr))) 4043 | lsl Rx(result_reg), Rx(result_reg), TMP1 4044 } else if (opcode == ZEND_DIV && 4045 (Z_MODE(op2_addr) == IS_CONST_ZVAL && 4046 zend_long_is_power_of_two(Z_LVAL_P(Z_ZV(op2_addr))))) { 4047 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4048 | asr Rx(result_reg), Rx(result_reg), #zend_long_floor_log2(Z_LVAL_P(Z_ZV(op2_addr))) 4049#if 0 4050 /* x86 specific optimizations through LEA instraction are not supported on ARM */ 4051 } else if (opcode == ZEND_ADD && 4052 !may_overflow && 4053 Z_MODE(op1_addr) == IS_REG && 4054 Z_MODE(op2_addr) == IS_CONST_ZVAL) { 4055 | NIY // TODO: test 4056 } else if (opcode == ZEND_ADD && 4057 !may_overflow && 4058 Z_MODE(op2_addr) == IS_REG && 4059 Z_MODE(op1_addr) == IS_CONST_ZVAL) { 4060 | NIY // TODO: test 4061 } else if (opcode == ZEND_SUB && 4062 !may_overflow && 4063 Z_MODE(op1_addr) == IS_REG && 4064 Z_MODE(op2_addr) == IS_CONST_ZVAL) { 4065 | NIY // TODO: test 4066#endif 4067 } else if (opcode == ZEND_MUL) { 4068 | GET_ZVAL_LVAL ZREG_TMP1, op1_addr, TMP1 4069 | GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2 4070 | mul Rx(result_reg), TMP1, TMP2 4071 if(may_overflow) { 4072 /* Use 'smulh' to get the upper 64 bits fo the 128-bit result. 4073 * For signed multiplication, the top 65 bits of the result will contain 4074 * either all zeros or all ones if no overflow occurred. 4075 * Flag: bne -> overflow. beq -> no overflow. 4076 */ 4077 use_ovf_flag = 0; 4078 | smulh TMP1, TMP1, TMP2 4079 | cmp TMP1, Rx(result_reg), asr #63 4080 } 4081 } else { 4082 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4083 if ((opcode == ZEND_ADD || opcode == ZEND_SUB) 4084 && Z_MODE(op2_addr) == IS_CONST_ZVAL 4085 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { 4086 /* +/- 0 */ 4087 may_overflow = 0; 4088 } else if (same_ops && opcode != ZEND_DIV) { 4089 | LONG_MATH_REG opcode, Rx(result_reg), Rx(result_reg), Rx(result_reg) 4090 } else { 4091 | LONG_MATH opcode, result_reg, op2_addr, TMP1 4092 } 4093 } 4094 if (may_overflow) { 4095 if (res_info & MAY_BE_GUARD) { 4096 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 4097 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 4098 if (!exit_addr) { 4099 return 0; 4100 } 4101 if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) { 4102 if (use_ovf_flag) { 4103 | bvs &exit_addr 4104 } else { 4105 | bne &exit_addr 4106 } 4107 if (Z_MODE(res_addr) == IS_REG && result_reg != Z_REG(res_addr)) { 4108 | mov Rx(Z_REG(res_addr)), Rx(result_reg) 4109 } 4110 } else if ((res_info & MAY_BE_ANY) == MAY_BE_DOUBLE) { 4111 if (use_ovf_flag) { 4112 | bvc &exit_addr 4113 } else { 4114 | beq &exit_addr 4115 } 4116 } else { 4117 ZEND_UNREACHABLE(); 4118 } 4119 } else { 4120 if (res_info & MAY_BE_LONG) { 4121 if (use_ovf_flag) { 4122 | bvs >1 4123 } else { 4124 | bne >1 4125 } 4126 } else { 4127 if (use_ovf_flag) { 4128 | bvc >1 4129 } else { 4130 | beq >1 4131 } 4132 } 4133 } 4134 } 4135 4136 if (Z_MODE(res_addr) == IS_MEM_ZVAL && (res_info & MAY_BE_LONG)) { 4137 | SET_ZVAL_LVAL_FROM_REG res_addr, Rx(result_reg), TMP1 4138 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) { 4139 if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) { 4140 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 4141 } 4142 } 4143 } 4144 4145 if (may_overflow && (!(res_info & MAY_BE_GUARD) || (res_info & MAY_BE_ANY) == MAY_BE_DOUBLE)) { 4146 zend_reg tmp_reg1 = ZREG_FPR0; 4147 zend_reg tmp_reg2 = ZREG_FPR1; 4148 4149 if (res_info & MAY_BE_LONG) { 4150 |.cold_code 4151 |1: 4152 } 4153 4154 do { 4155 if ((Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 1) || 4156 (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 1)) { 4157 if (opcode == ZEND_ADD) { 4158 uint64_t val = 0x43e0000000000000; 4159 if (Z_MODE(res_addr) == IS_REG) { 4160 | LOAD_64BIT_VAL TMP1, val 4161 | fmov Rd(Z_REG(res_addr)-ZREG_V0), TMP1 4162 } else { 4163 | SET_ZVAL_LVAL res_addr, val, TMP2, TMP1 4164 } 4165 break; 4166 } else if (opcode == ZEND_SUB) { 4167 uint64_t val = 0xc3e0000000000000; 4168 if (Z_MODE(res_addr) == IS_REG) { 4169 | LOAD_64BIT_VAL TMP1, val 4170 | fmov Rd(Z_REG(res_addr)-ZREG_V0), TMP1 4171 } else { 4172 | SET_ZVAL_LVAL res_addr, val, TMP2, TMP1 4173 } 4174 break; 4175 } 4176 } 4177 4178 | DOUBLE_GET_ZVAL_LVAL tmp_reg1, op1_addr, tmp_reg, ZREG_TMP1 4179 | DOUBLE_GET_ZVAL_LVAL tmp_reg2, op2_addr, tmp_reg, ZREG_TMP1 4180 | DOUBLE_MATH_REG opcode, tmp_reg1, tmp_reg1, tmp_reg2 4181 | SET_ZVAL_DVAL res_addr, tmp_reg1, ZREG_TMP1 4182 } while (0); 4183 4184 if (Z_MODE(res_addr) == IS_MEM_ZVAL 4185 && (res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) { 4186 | SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2 4187 } 4188 if (res_info & MAY_BE_LONG) { 4189 | b >2 4190 |.code 4191 } 4192 |2: 4193 } 4194 4195 return 1; 4196} 4197 4198static int zend_jit_math_long_double(dasm_State **Dst, 4199 zend_uchar opcode, 4200 zend_jit_addr op1_addr, 4201 zend_jit_addr op2_addr, 4202 zend_jit_addr res_addr, 4203 uint32_t res_use_info) 4204{ 4205 zend_reg result_reg = 4206 (Z_MODE(res_addr) == IS_REG) ? Z_REG(res_addr) : ZREG_FPR0; 4207 zend_reg op2_reg; 4208 4209 | DOUBLE_GET_ZVAL_LVAL result_reg, op1_addr, ZREG_TMP1, ZREG_TMP2 4210 4211 if (Z_MODE(op2_addr) == IS_REG) { 4212 op2_reg = Z_REG(op2_addr); 4213 } else { 4214 op2_reg = ZREG_FPTMP; 4215 | GET_ZVAL_DVAL op2_reg, op2_addr, ZREG_TMP1 4216 } 4217 4218 | DOUBLE_MATH_REG opcode, result_reg, result_reg, op2_reg 4219 4220 | SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1 4221 4222 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 4223 if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) { 4224 | SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2 4225 } 4226 } 4227 4228 return 1; 4229} 4230 4231static int zend_jit_math_double_long(dasm_State **Dst, 4232 zend_uchar opcode, 4233 zend_jit_addr op1_addr, 4234 zend_jit_addr op2_addr, 4235 zend_jit_addr res_addr, 4236 uint32_t res_use_info) 4237{ 4238 zend_reg result_reg, op1_reg, op2_reg; 4239 4240 if (zend_is_commutative(opcode) 4241 && (Z_MODE(res_addr) != IS_REG || Z_MODE(op1_addr) != IS_REG || Z_REG(res_addr) != Z_REG(op1_addr))) { 4242 if (Z_MODE(res_addr) == IS_REG) { 4243 result_reg = Z_REG(res_addr); 4244 } else { 4245 result_reg = ZREG_FPR0; 4246 } 4247 | DOUBLE_GET_ZVAL_LVAL result_reg, op2_addr, ZREG_TMP1, ZREG_TMP2 4248 if (Z_MODE(op1_addr) == IS_REG) { 4249 op1_reg = Z_REG(op1_addr); 4250 } else { 4251 op1_reg = ZREG_FPTMP; 4252 | GET_ZVAL_DVAL op1_reg, op1_addr, ZREG_TMP1 4253 } 4254 | DOUBLE_MATH_REG opcode, result_reg, result_reg, op1_reg 4255 } else { 4256 if (Z_MODE(res_addr) == IS_REG) { 4257 result_reg = Z_REG(res_addr); 4258 } else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) { 4259 result_reg = Z_REG(op1_addr); 4260 } else { 4261 result_reg = ZREG_FPR0; 4262 } 4263 4264 if (Z_MODE(op1_addr) == IS_REG) { 4265 op1_reg = Z_REG(op1_addr); 4266 } else { 4267 | GET_ZVAL_DVAL result_reg, op1_addr, ZREG_TMP1 4268 op1_reg = result_reg; 4269 } 4270 if ((opcode == ZEND_ADD || opcode == ZEND_SUB) 4271 && Z_MODE(op2_addr) == IS_CONST_ZVAL 4272 && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { 4273 /* +/- 0 */ 4274 } else { 4275 op2_reg = ZREG_FPTMP; 4276 | DOUBLE_GET_ZVAL_LVAL op2_reg, op2_addr, ZREG_TMP1, ZREG_TMP2 4277 | DOUBLE_MATH_REG opcode, result_reg, op1_reg, op2_reg 4278 } 4279 } 4280 4281 | SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1 4282 4283 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 4284 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) { 4285 if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) { 4286 | SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2 4287 } 4288 } 4289 } 4290 4291 return 1; 4292} 4293 4294static int zend_jit_math_double_double(dasm_State **Dst, 4295 zend_uchar opcode, 4296 zend_jit_addr op1_addr, 4297 zend_jit_addr op2_addr, 4298 zend_jit_addr res_addr, 4299 uint32_t res_use_info) 4300{ 4301 bool same_ops = zend_jit_same_addr(op1_addr, op2_addr); 4302 zend_reg result_reg, op1_reg, op2_reg; 4303 zend_jit_addr val_addr; 4304 4305 if (Z_MODE(res_addr) == IS_REG) { 4306 result_reg = Z_REG(res_addr); 4307 } else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) { 4308 result_reg = Z_REG(op1_addr); 4309 } else if (zend_is_commutative(opcode) && Z_MODE(op2_addr) == IS_REG && Z_LAST_USE(op2_addr)) { 4310 result_reg = Z_REG(op2_addr); 4311 } else { 4312 result_reg = ZREG_FPR0; 4313 } 4314 4315 if (Z_MODE(op1_addr) == IS_REG) { 4316 op1_reg = Z_REG(op1_addr); 4317 val_addr = op2_addr; 4318 } else if (Z_MODE(op2_addr) == IS_REG && zend_is_commutative(opcode)) { 4319 op1_reg = Z_REG(op2_addr); 4320 val_addr = op1_addr; 4321 } else { 4322 | GET_ZVAL_DVAL result_reg, op1_addr, ZREG_TMP1 4323 op1_reg = result_reg; 4324 val_addr = op2_addr; 4325 } 4326 4327 if ((opcode == ZEND_MUL) && 4328 Z_MODE(val_addr) == IS_CONST_ZVAL && Z_DVAL_P(Z_ZV(val_addr)) == 2.0) { 4329 | DOUBLE_MATH_REG ZEND_ADD, result_reg, op1_reg, op1_reg 4330 } else { 4331 if (same_ops) { 4332 op2_reg = op1_reg; 4333 } else if (Z_MODE(val_addr) == IS_REG) { 4334 op2_reg = Z_REG(val_addr); 4335 } else { 4336 op2_reg = ZREG_FPTMP; 4337 | GET_ZVAL_DVAL op2_reg, val_addr, ZREG_TMP1 4338 } 4339 | DOUBLE_MATH_REG opcode, result_reg, op1_reg, op2_reg 4340 } 4341 4342 | SET_ZVAL_DVAL res_addr, result_reg, ZREG_TMP1 4343 4344 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 4345 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) { 4346 if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_DOUBLE) { 4347 | SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2 4348 } 4349 } 4350 } 4351 return 1; 4352} 4353 4354static int zend_jit_math_helper(dasm_State **Dst, 4355 const zend_op *opline, 4356 zend_uchar opcode, 4357 zend_uchar op1_type, 4358 znode_op op1, 4359 zend_jit_addr op1_addr, 4360 uint32_t op1_info, 4361 zend_uchar op2_type, 4362 znode_op op2, 4363 zend_jit_addr op2_addr, 4364 uint32_t op2_info, 4365 uint32_t res_var, 4366 zend_jit_addr res_addr, 4367 uint32_t res_info, 4368 uint32_t res_use_info, 4369 int may_overflow, 4370 int may_throw) 4371/* Labels: 1,2,3,4,5,6 */ 4372{ 4373 bool same_ops = zend_jit_same_addr(op1_addr, op2_addr); 4374 4375 if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) { 4376 if (op1_info & (MAY_BE_ANY-MAY_BE_LONG)) { 4377 if (op1_info & MAY_BE_DOUBLE) { 4378 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1 4379 } else { 4380 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1 4381 } 4382 } 4383 if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_LONG))) { 4384 if (op2_info & MAY_BE_DOUBLE) { 4385 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >1, ZREG_TMP1 4386 |.cold_code 4387 |1: 4388 if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) { 4389 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1 4390 } 4391 if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) { 4392 return 0; 4393 } 4394 | b >5 4395 |.code 4396 } else { 4397 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1 4398 } 4399 } 4400 if (!zend_jit_math_long_long(Dst, opline, opcode, op1_addr, op2_addr, res_addr, res_info, res_use_info, may_overflow)) { 4401 return 0; 4402 } 4403 if (op1_info & MAY_BE_DOUBLE) { 4404 |.cold_code 4405 |3: 4406 if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) { 4407 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1 4408 } 4409 if (op2_info & MAY_BE_DOUBLE) { 4410 if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) { 4411 if (!same_ops) { 4412 | IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >1, ZREG_TMP1 4413 } else { 4414 | IF_NOT_ZVAL_TYPE, op2_addr, IS_DOUBLE, >6, ZREG_TMP1 4415 } 4416 } 4417 if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) { 4418 return 0; 4419 } 4420 | b >5 4421 } 4422 if (!same_ops) { 4423 |1: 4424 if (op2_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_DOUBLE))) { 4425 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1 4426 } 4427 if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) { 4428 return 0; 4429 } 4430 | b >5 4431 } 4432 |.code 4433 } 4434 } else if ((op1_info & MAY_BE_DOUBLE) && 4435 !(op1_info & MAY_BE_LONG) && 4436 (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 4437 (res_info & MAY_BE_DOUBLE)) { 4438 if (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) { 4439 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1 4440 } 4441 if (op2_info & MAY_BE_DOUBLE) { 4442 if (!same_ops && (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) { 4443 if (!same_ops && (op2_info & MAY_BE_LONG)) { 4444 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >1, ZREG_TMP1 4445 } else { 4446 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1 4447 } 4448 } 4449 if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) { 4450 return 0; 4451 } 4452 } 4453 if (!same_ops && (op2_info & MAY_BE_LONG)) { 4454 if (op2_info & MAY_BE_DOUBLE) { 4455 |.cold_code 4456 } 4457 |1: 4458 if (op2_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) { 4459 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1 4460 } 4461 if (!zend_jit_math_double_long(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) { 4462 return 0; 4463 } 4464 if (op2_info & MAY_BE_DOUBLE) { 4465 | b >5 4466 |.code 4467 } 4468 } 4469 } else if ((op2_info & MAY_BE_DOUBLE) && 4470 !(op2_info & MAY_BE_LONG) && 4471 (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 4472 (res_info & MAY_BE_DOUBLE)) { 4473 if (op2_info & (MAY_BE_ANY-MAY_BE_DOUBLE)) { 4474 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >6, ZREG_TMP1 4475 } 4476 if (op1_info & MAY_BE_DOUBLE) { 4477 if (!same_ops && (op1_info & (MAY_BE_ANY-MAY_BE_DOUBLE))) { 4478 if (!same_ops && (op1_info & MAY_BE_LONG)) { 4479 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >1, ZREG_TMP1 4480 } else { 4481 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >6, ZREG_TMP1 4482 } 4483 } 4484 if (!zend_jit_math_double_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) { 4485 return 0; 4486 } 4487 } 4488 if (!same_ops && (op1_info & MAY_BE_LONG)) { 4489 if (op1_info & MAY_BE_DOUBLE) { 4490 |.cold_code 4491 } 4492 |1: 4493 if (op1_info & (MAY_BE_ANY-(MAY_BE_DOUBLE|MAY_BE_LONG))) { 4494 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1 4495 } 4496 if (!zend_jit_math_long_double(Dst, opcode, op1_addr, op2_addr, res_addr, res_use_info)) { 4497 return 0; 4498 } 4499 if (op1_info & MAY_BE_DOUBLE) { 4500 | b >5 4501 |.code 4502 } 4503 } 4504 } 4505 4506 |5: 4507 4508 if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) || 4509 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) { 4510 if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 4511 (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 4512 (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) { 4513 |.cold_code 4514 } 4515 |6: 4516 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) { 4517 if (Z_MODE(res_addr) == IS_REG) { 4518 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var); 4519 | LOAD_ZVAL_ADDR FCARG1x, real_addr 4520 } else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 4521 | LOAD_ZVAL_ADDR FCARG1x, res_addr 4522 } 4523 if (Z_MODE(op1_addr) == IS_REG) { 4524 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var); 4525 if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) { 4526 return 0; 4527 } 4528 op1_addr = real_addr; 4529 } 4530 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 4531 } else { 4532 if (Z_MODE(op1_addr) == IS_REG) { 4533 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var); 4534 if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) { 4535 return 0; 4536 } 4537 op1_addr = real_addr; 4538 } 4539 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 4540 if (Z_MODE(res_addr) == IS_REG) { 4541 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var); 4542 | LOAD_ZVAL_ADDR FCARG1x, real_addr 4543 } else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 4544 | LOAD_ZVAL_ADDR FCARG1x, res_addr 4545 } 4546 } 4547 if (Z_MODE(op2_addr) == IS_REG) { 4548 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var); 4549 if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) { 4550 return 0; 4551 } 4552 op2_addr = real_addr; 4553 } 4554 | LOAD_ZVAL_ADDR CARG3, op2_addr 4555 | SET_EX_OPLINE opline, REG0 4556 if (opcode == ZEND_ADD) { 4557 | EXT_CALL add_function, REG0 4558 } else if (opcode == ZEND_SUB) { 4559 | EXT_CALL sub_function, REG0 4560 } else if (opcode == ZEND_MUL) { 4561 | EXT_CALL mul_function, REG0 4562 } else if (opcode == ZEND_DIV) { 4563 | EXT_CALL div_function, REG0 4564 } else { 4565 ZEND_UNREACHABLE(); 4566 } 4567 | FREE_OP op1_type, op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 4568 | FREE_OP op2_type, op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 4569 if (may_throw) { 4570 if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) { 4571 | MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1 4572 | cbnz TMP2, ->exception_handler_free_op2 4573 } else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) { 4574 zend_jit_check_exception_undef_result(Dst, opline); 4575 } else { 4576 zend_jit_check_exception(Dst); 4577 } 4578 } 4579 if (Z_MODE(res_addr) == IS_REG) { 4580 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var); 4581 if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) { 4582 return 0; 4583 } 4584 } 4585 if ((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 4586 (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 4587 (res_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) { 4588 | b <5 4589 |.code 4590 } 4591 } 4592 4593 return 1; 4594} 4595 4596static 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) 4597{ 4598 ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF)); 4599 ZEND_ASSERT((op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 4600 (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))); 4601 4602 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)) { 4603 return 0; 4604 } 4605 if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) { 4606 return 0; 4607 } 4608 return 1; 4609} 4610 4611static 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) 4612{ 4613 if (Z_MODE(op2_addr) != IS_MEM_ZVAL || Z_REG(op2_addr) != ZREG_FCARG1) { 4614 | GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1 4615 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 4616 } else if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) { 4617 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 4618 | GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1 4619 } else { 4620 | GET_ZVAL_LVAL ZREG_REG0, op2_addr, TMP1 4621 | GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1 4622 | mov FCARG2x, REG0 4623 } 4624 | EXT_CALL zend_jit_add_arrays_helper, REG0 4625 | SET_ZVAL_PTR res_addr, RETVALx, TMP1 4626 | SET_ZVAL_TYPE_INFO res_addr, IS_ARRAY_EX, TMP1w, TMP2 4627 | FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2 4628 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 4629 return 1; 4630} 4631 4632static int zend_jit_long_math_helper(dasm_State **Dst, 4633 const zend_op *opline, 4634 zend_uchar opcode, 4635 zend_uchar op1_type, 4636 znode_op op1, 4637 zend_jit_addr op1_addr, 4638 uint32_t op1_info, 4639 zend_ssa_range *op1_range, 4640 zend_uchar op2_type, 4641 znode_op op2, 4642 zend_jit_addr op2_addr, 4643 uint32_t op2_info, 4644 zend_ssa_range *op2_range, 4645 uint32_t res_var, 4646 zend_jit_addr res_addr, 4647 uint32_t res_info, 4648 uint32_t res_use_info, 4649 int may_throw) 4650/* Labels: 6 */ 4651{ 4652 bool same_ops = zend_jit_same_addr(op1_addr, op2_addr); 4653 zend_reg result_reg; 4654 4655 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) { 4656 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1 4657 } 4658 if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) { 4659 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >6, ZREG_TMP1 4660 } 4661 4662 if (Z_MODE(res_addr) == IS_REG) { 4663 if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) 4664 && opline->op2_type != IS_CONST) { 4665 result_reg = ZREG_REG0; 4666 } else { 4667 result_reg = Z_REG(res_addr); 4668 } 4669 } else if (Z_MODE(op1_addr) == IS_REG && Z_LAST_USE(op1_addr)) { 4670 result_reg = Z_REG(op1_addr); 4671 } else if (Z_REG(res_addr) != ZREG_REG0) { 4672 result_reg = ZREG_REG0; 4673 } else { 4674 /* ASSIGN_DIM_OP */ 4675 result_reg = ZREG_FCARG1; 4676 } 4677 4678 if (opcode == ZEND_SL) { 4679 if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { 4680 zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr)); 4681 4682 if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) { 4683 if (EXPECTED(op2_lval > 0)) { 4684 | mov Rx(result_reg), xzr 4685 } else { 4686 zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1); 4687 zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2); 4688 | SET_EX_OPLINE opline, REG0 4689 | b ->negative_shift 4690 } 4691 } else if (Z_MODE(op1_addr) == IS_REG && op2_lval == 1) { 4692 | add Rx(result_reg), Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr)) 4693 } else { 4694 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4695 | lsl Rx(result_reg), Rx(result_reg), #op2_lval 4696 } 4697 } else { 4698 zend_reg op2_reg; 4699 4700 if (Z_MODE(op2_addr) == IS_REG) { 4701 op2_reg = Z_REG(op2_addr); 4702 } else { 4703 op2_reg = ZREG_TMP2; 4704 | GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2 4705 } 4706 if (!op2_range || 4707 op2_range->min < 0 || 4708 op2_range->max >= SIZEOF_ZEND_LONG * 8) { 4709 4710 | cmp Rx(op2_reg), #(SIZEOF_ZEND_LONG*8) 4711 | bhs >1 4712 |.cold_code 4713 |1: 4714 | mov Rx(result_reg), xzr 4715 | cmp Rx(op2_reg), xzr 4716 | bgt >1 4717 zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1); 4718 zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2); 4719 | SET_EX_OPLINE opline, REG0 4720 | b ->negative_shift 4721 |.code 4722 } 4723 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4724 | lsl Rx(result_reg), Rx(result_reg), Rx(op2_reg) 4725 |1: 4726 } 4727 } else if (opcode == ZEND_SR) { 4728 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4729 if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { 4730 zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr)); 4731 4732 if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) { 4733 if (EXPECTED(op2_lval > 0)) { 4734 | asr Rx(result_reg), Rx(result_reg), #((SIZEOF_ZEND_LONG * 8) - 1) 4735 } else { 4736 zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1); 4737 zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2); 4738 | SET_EX_OPLINE opline, REG0 4739 | b ->negative_shift 4740 } 4741 } else { 4742 | asr Rx(result_reg), Rx(result_reg), #op2_lval 4743 } 4744 } else { 4745 zend_reg op2_reg; 4746 4747 if (Z_MODE(op2_addr) == IS_REG) { 4748 op2_reg = Z_REG(op2_addr); 4749 } else { 4750 op2_reg = ZREG_TMP2; 4751 | GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2 4752 } 4753 if (!op2_range || 4754 op2_range->min < 0 || 4755 op2_range->max >= SIZEOF_ZEND_LONG * 8) { 4756 | cmp Rx(op2_reg), #(SIZEOF_ZEND_LONG*8) 4757 | bhs >1 4758 |.cold_code 4759 |1: 4760 | cmp Rx(op2_reg), xzr 4761 | mov Rx(op2_reg), #((SIZEOF_ZEND_LONG * 8) - 1) 4762 | bgt >1 4763 zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1); 4764 zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2); 4765 | SET_EX_OPLINE opline, REG0 4766 | b ->negative_shift 4767 |.code 4768 } 4769 |1: 4770 | asr Rx(result_reg), Rx(result_reg), Rx(op2_reg) 4771 } 4772 } else if (opcode == ZEND_MOD) { 4773 if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { 4774 zend_long op2_lval = Z_LVAL_P(Z_ZV(op2_addr)); 4775 4776 if (op2_lval == 0) { 4777 zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1); 4778 zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2); 4779 | SET_EX_OPLINE opline, REG0 4780 | b ->mod_by_zero 4781 } else if (op2_lval == -1) { 4782 | mov Rx(result_reg), xzr 4783 } else if (zend_long_is_power_of_two(op2_lval) && op1_range && op1_range->min >= 0) { 4784 zval tmp; 4785 zend_jit_addr tmp_addr; 4786 4787 /* Optimisation for mod of power of 2 */ 4788 ZVAL_LONG(&tmp, op2_lval - 1); 4789 tmp_addr = ZEND_ADDR_CONST_ZVAL(&tmp); 4790 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4791 | LONG_MATH ZEND_BW_AND, result_reg, tmp_addr, TMP1 4792 } else { 4793 | GET_ZVAL_LVAL ZREG_TMP1, op1_addr, TMP1 4794 | GET_ZVAL_LVAL ZREG_TMP2, op2_addr, TMP2 4795 | sdiv Rx(result_reg), TMP1, TMP2 4796 | msub Rx(result_reg), Rx(result_reg), TMP2, TMP1 4797 } 4798 } else { 4799 zend_reg op2_reg; 4800 4801 if (Z_MODE(op2_addr) == IS_MEM_ZVAL) { 4802 | MEM_ACCESS_64_WITH_UOFFSET ldr, TMP2, Rx(Z_REG(op2_addr)), Z_OFFSET(op2_addr), TMP2 4803 op2_reg = ZREG_TMP2; 4804 } else { 4805 ZEND_ASSERT(Z_MODE(op2_addr) == IS_REG); 4806 op2_reg = Z_REG(op2_addr); 4807 } 4808 4809 if ((op2_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) || !op2_range || (op2_range->min <= 0 && op2_range->max >= 0)) { 4810 | cbz Rx(op2_reg), >1 4811 |.cold_code 4812 |1: 4813 zend_jit_invalidate_var_if_necessary(Dst, op1_type, op1_addr, op1); 4814 zend_jit_invalidate_var_if_necessary(Dst, op2_type, op2_addr, op2); 4815 | SET_EX_OPLINE opline, REG0 4816 | b ->mod_by_zero 4817 |.code 4818 } 4819 4820 /* Prevent overflow error/crash if op1 == LONG_MIN and op2 == -1 */ 4821 if (!op2_range || (op2_range->min <= -1 && op2_range->max >= -1)) { 4822 | cmn Rx(op2_reg), #1 4823 | beq >1 4824 |.cold_code 4825 |1: 4826 | SET_ZVAL_LVAL_FROM_REG res_addr, xzr, TMP1 4827 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 4828 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) { 4829 if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) { 4830 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 4831 } 4832 } 4833 } 4834 | b >5 4835 |.code 4836 } 4837 4838 | GET_ZVAL_LVAL ZREG_TMP1, op1_addr, TMP1 4839 | sdiv Rx(result_reg), TMP1, Rx(op2_reg) 4840 | msub Rx(result_reg), Rx(result_reg), Rx(op2_reg), TMP1 4841 } 4842 } else if (same_ops) { 4843 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4844 | LONG_MATH_REG opcode, Rx(result_reg), Rx(result_reg), Rx(result_reg) 4845 } else { 4846 | GET_ZVAL_LVAL result_reg, op1_addr, TMP1 4847 | LONG_MATH opcode, result_reg, op2_addr, TMP1 4848 } 4849 4850 if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != result_reg) { 4851 | SET_ZVAL_LVAL_FROM_REG res_addr, Rx(result_reg), TMP1 4852 } 4853 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 4854 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != Z_REG(res_addr) || Z_OFFSET(op1_addr) != Z_OFFSET(res_addr)) { 4855 if ((res_use_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_GUARD)) != MAY_BE_LONG) { 4856 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 4857 } 4858 } 4859 } 4860 4861 if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) || 4862 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) { 4863 if ((op1_info & MAY_BE_LONG) && 4864 (op2_info & MAY_BE_LONG)) { 4865 |.cold_code 4866 } 4867 |6: 4868 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) { 4869 if (Z_MODE(res_addr) == IS_REG) { 4870 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var); 4871 | LOAD_ZVAL_ADDR FCARG1x, real_addr 4872 } else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 4873 | LOAD_ZVAL_ADDR FCARG1x, res_addr 4874 } 4875 if (Z_MODE(op1_addr) == IS_REG) { 4876 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var); 4877 if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) { 4878 return 0; 4879 } 4880 op1_addr = real_addr; 4881 } 4882 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 4883 } else { 4884 if (Z_MODE(op1_addr) == IS_REG) { 4885 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op1.var); 4886 if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) { 4887 return 0; 4888 } 4889 op1_addr = real_addr; 4890 } 4891 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 4892 if (Z_MODE(res_addr) == IS_REG) { 4893 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var); 4894 | LOAD_ZVAL_ADDR FCARG1x, real_addr 4895 } else if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 4896 | LOAD_ZVAL_ADDR FCARG1x, res_addr 4897 } 4898 } 4899 if (Z_MODE(op2_addr) == IS_REG) { 4900 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, op2.var); 4901 if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) { 4902 return 0; 4903 } 4904 op2_addr = real_addr; 4905 } 4906 | LOAD_ZVAL_ADDR CARG3, op2_addr 4907 | SET_EX_OPLINE opline, REG0 4908 if (opcode == ZEND_BW_OR) { 4909 | EXT_CALL bitwise_or_function, REG0 4910 } else if (opcode == ZEND_BW_AND) { 4911 | EXT_CALL bitwise_and_function, REG0 4912 } else if (opcode == ZEND_BW_XOR) { 4913 | EXT_CALL bitwise_xor_function, REG0 4914 } else if (opcode == ZEND_SL) { 4915 | EXT_CALL shift_left_function, REG0 4916 } else if (opcode == ZEND_SR) { 4917 | EXT_CALL shift_right_function, REG0 4918 } else if (opcode == ZEND_MOD) { 4919 | EXT_CALL mod_function, REG0 4920 } else { 4921 ZEND_UNREACHABLE(); 4922 } 4923 if (op1_addr == res_addr && (op2_info & MAY_BE_RCN)) { 4924 /* compound assignment may decrement "op2" refcount */ 4925 op2_info |= MAY_BE_RC1; 4926 } 4927 | FREE_OP op1_type, op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 4928 | FREE_OP op2_type, op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 4929 if (may_throw) { 4930 if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) { 4931 | MEM_LOAD_64_ZTS ldr, TMP2, executor_globals, exception, TMP1 4932 | cbnz TMP2, ->exception_handler_free_op2 4933 } else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) { 4934 zend_jit_check_exception_undef_result(Dst, opline); 4935 } else { 4936 zend_jit_check_exception(Dst); 4937 } 4938 } 4939 if (Z_MODE(res_addr) == IS_REG) { 4940 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, res_var); 4941 if (!zend_jit_load_reg(Dst, real_addr, res_addr, res_info)) { 4942 return 0; 4943 } 4944 } 4945 if ((op1_info & MAY_BE_LONG) && 4946 (op2_info & MAY_BE_LONG)) { 4947 | b >5 4948 |.code 4949 } 4950 } 4951 |5: 4952 4953 return 1; 4954} 4955 4956static 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) 4957{ 4958 ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF)); 4959 ZEND_ASSERT((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)); 4960 4961 if (!zend_jit_long_math_helper(Dst, opline, opline->opcode, 4962 opline->op1_type, opline->op1, op1_addr, op1_info, op1_range, 4963 opline->op2_type, opline->op2, op2_addr, op2_info, op2_range, 4964 opline->result.var, res_addr, res_info, res_use_info, may_throw)) { 4965 return 0; 4966 } 4967 if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) { 4968 return 0; 4969 } 4970 return 1; 4971} 4972 4973static int zend_jit_concat_helper(dasm_State **Dst, 4974 const zend_op *opline, 4975 zend_uchar op1_type, 4976 znode_op op1, 4977 zend_jit_addr op1_addr, 4978 uint32_t op1_info, 4979 zend_uchar op2_type, 4980 znode_op op2, 4981 zend_jit_addr op2_addr, 4982 uint32_t op2_info, 4983 zend_jit_addr res_addr, 4984 int may_throw) 4985{ 4986 if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) { 4987 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) { 4988 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1 4989 } 4990 if (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) { 4991 | IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >6, ZREG_TMP1 4992 } 4993 if (Z_MODE(op1_addr) == IS_MEM_ZVAL && Z_REG(op1_addr) == Z_REG(res_addr) && Z_OFFSET(op1_addr) == Z_OFFSET(res_addr)) { 4994 if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 4995 | LOAD_ZVAL_ADDR FCARG1x, res_addr 4996 } 4997 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 4998 | EXT_CALL zend_jit_fast_assign_concat_helper, REG0 4999 /* concatenation with itself may reduce refcount */ 5000 op2_info |= MAY_BE_RC1; 5001 } else { 5002 if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 5003 | LOAD_ZVAL_ADDR FCARG1x, res_addr 5004 } 5005 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 5006 | LOAD_ZVAL_ADDR CARG3, op2_addr 5007 if (op1_type == IS_CV || op1_type == IS_CONST) { 5008 | EXT_CALL zend_jit_fast_concat_helper, REG0 5009 } else { 5010 | EXT_CALL zend_jit_fast_concat_tmp_helper, REG0 5011 } 5012 } 5013 /* concatenation with empty string may increase refcount */ 5014 op2_info |= MAY_BE_RCN; 5015 | FREE_OP op2_type, op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 5016 |5: 5017 } 5018 if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING)) || 5019 (op2_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF) - MAY_BE_STRING))) { 5020 if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) { 5021 |.cold_code 5022 |6: 5023 } 5024 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1) { 5025 if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 5026 | LOAD_ZVAL_ADDR FCARG1x, res_addr 5027 } 5028 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 5029 } else { 5030 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 5031 if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 5032 | LOAD_ZVAL_ADDR FCARG1x, res_addr 5033 } 5034 } 5035 | LOAD_ZVAL_ADDR CARG3, op2_addr 5036 | SET_EX_OPLINE opline, REG0 5037 | EXT_CALL concat_function, REG0 5038 /* concatenation with empty string may increase refcount */ 5039 op1_info |= MAY_BE_RCN; 5040 op2_info |= MAY_BE_RCN; 5041 | FREE_OP op1_type, op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 5042 | FREE_OP op2_type, op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 5043 if (may_throw) { 5044 if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) { 5045 zend_jit_check_exception_undef_result(Dst, opline); 5046 } else { 5047 zend_jit_check_exception(Dst); 5048 } 5049 } 5050 if ((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)) { 5051 | b <5 5052 |.code 5053 } 5054 } 5055 5056 return 1; 5057} 5058 5059static 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) 5060{ 5061 zend_jit_addr op1_addr, op2_addr; 5062 5063 ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF)); 5064 ZEND_ASSERT((op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING)); 5065 5066 op1_addr = OP1_ADDR(); 5067 op2_addr = OP2_ADDR(); 5068 5069 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); 5070} 5071 5072static 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) 5073/* Labels: 1,2,3,4,5 */ 5074{ 5075 zend_jit_addr op2_addr = OP2_ADDR(); 5076 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 5077 5078 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 5079 && type == BP_VAR_R 5080 && !exit_addr) { 5081 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 5082 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 5083 if (!exit_addr) { 5084 return 0; 5085 } 5086 } 5087 5088 if (op2_info & MAY_BE_LONG) { 5089 bool op2_loaded = 0; 5090 bool packed_loaded = 0; 5091 bool bad_packed_key = 0; 5092 5093 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) { 5094 | // if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) 5095 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3, ZREG_TMP1 5096 } 5097 if (op1_info & MAY_BE_PACKED_GUARD) { 5098 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD); 5099 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 5100 5101 if (!exit_addr) { 5102 return 0; 5103 } 5104 if (op1_info & MAY_BE_ARRAY_PACKED) { 5105 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 5106 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 5107 | beq &exit_addr 5108 } else { 5109 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 5110 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 5111 | bne &exit_addr 5112 } 5113 } 5114 if (type == BP_VAR_W) { 5115 | // hval = Z_LVAL_P(dim); 5116 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 5117 op2_loaded = 1; 5118 } 5119 if (op1_info & MAY_BE_ARRAY_PACKED) { 5120 zend_long val = -1; 5121 5122 if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { 5123 val = Z_LVAL_P(Z_ZV(op2_addr)); 5124 if (val >= 0 && val < HT_MAX_SIZE) { 5125 packed_loaded = 1; 5126 } else { 5127 bad_packed_key = 1; 5128 } 5129 } else { 5130 if (!op2_loaded) { 5131 | // hval = Z_LVAL_P(dim); 5132 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 5133 op2_loaded = 1; 5134 } 5135 packed_loaded = 1; 5136 } 5137 5138 if (dim_type == IS_UNDEF && type == BP_VAR_W) { 5139 /* don't generate "fast" code for packed array */ 5140 packed_loaded = 0; 5141 } 5142 5143 if (packed_loaded) { 5144 | // ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef); 5145 if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) { 5146 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 5147 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 5148 | beq >4 // HASH_FIND 5149 } 5150 | // if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed)) 5151 5152 | ldr REG0w, [FCARG1x, #offsetof(zend_array, nNumUsed)] 5153 if (val == 0) { 5154 | cmp REG0, xzr 5155 } else if (val > 0 && !op2_loaded) { 5156 | CMP_64_WITH_CONST REG0, val, TMP1 5157 } else { 5158 | cmp REG0, FCARG2x 5159 } 5160 5161 if (type == BP_JIT_IS) { 5162 if (not_found_exit_addr) { 5163 | bls ¬_found_exit_addr 5164 } else { 5165 | bls >9 // NOT_FOUND 5166 } 5167 } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { 5168 | bls &exit_addr 5169 } else if (type == BP_VAR_IS && not_found_exit_addr) { 5170 | bls ¬_found_exit_addr 5171 } else if (type == BP_VAR_RW && not_found_exit_addr) { 5172 | bls ¬_found_exit_addr 5173 } else if (type == BP_VAR_IS && found_exit_addr) { 5174 | bls >7 // NOT_FOUND 5175 } else { 5176 | bls >2 // NOT_FOUND 5177 } 5178 | // _ret = &_ht->arPacked[_h].val; 5179 if (val >= 0) { 5180 | ldr REG0, [FCARG1x, #offsetof(zend_array, arPacked)] 5181 if (val != 0) { 5182 | ADD_SUB_64_WITH_CONST add, REG0, REG0, (val * sizeof(zval)), TMP1 5183 } 5184 } else { 5185 | ldr TMP1, [FCARG1x, #offsetof(zend_array, arPacked)] 5186 | add REG0, TMP1, FCARG2x, lsl #4 5187 } 5188 } 5189 } 5190 switch (type) { 5191 case BP_JIT_IS: 5192 if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) { 5193 if (packed_loaded) { 5194 | b >5 5195 } 5196 |4: 5197 if (!op2_loaded) { 5198 | // hval = Z_LVAL_P(dim); 5199 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 5200 } 5201 if (packed_loaded) { 5202 | EXT_CALL _zend_hash_index_find, REG0 5203 } else { 5204 | EXT_CALL zend_hash_index_find, REG0 5205 } 5206 | mov REG0, RETVALx 5207 if (not_found_exit_addr) { 5208 | cbz REG0, ¬_found_exit_addr 5209 } else { 5210 | cbz REG0, >9 // NOT_FOUND 5211 } 5212 if (op2_info & MAY_BE_STRING) { 5213 | b >5 5214 } 5215 } else if (packed_loaded) { 5216 if (op2_info & MAY_BE_STRING) { 5217 | b >5 5218 } 5219 } else if (not_found_exit_addr) { 5220 | b ¬_found_exit_addr 5221 } else { 5222 | b >9 // NOT_FOUND 5223 } 5224 break; 5225 case BP_VAR_R: 5226 case BP_VAR_IS: 5227 case BP_VAR_UNSET: 5228 if (packed_loaded) { 5229 if (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) { 5230 | IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w 5231 } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { 5232 /* perform IS_UNDEF check only after result type guard (during deoptimization) */ 5233 if (!found_exit_addr || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) { 5234 | IF_Z_TYPE REG0, IS_UNDEF, &exit_addr, TMP1w 5235 } 5236 } else if (type == BP_VAR_IS && not_found_exit_addr) { 5237 | IF_Z_TYPE REG0, IS_UNDEF, ¬_found_exit_addr, TMP1w 5238 } else if (type == BP_VAR_IS && found_exit_addr) { 5239 | IF_Z_TYPE REG0, IS_UNDEF, >7, TMP1w // NOT_FOUND 5240 } else { 5241 | IF_Z_TYPE REG0, IS_UNDEF, >2, TMP1w // NOT_FOUND 5242 } 5243 } 5244 if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (packed_loaded && (op1_info & MAY_BE_ARRAY_NUMERIC_HASH))) { 5245 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { 5246 | b &exit_addr 5247 } else if (type == BP_VAR_IS && not_found_exit_addr) { 5248 | b ¬_found_exit_addr 5249 } else if (type == BP_VAR_IS && found_exit_addr) { 5250 | b >7 // NOT_FOUND 5251 } else { 5252 | b >2 // NOT_FOUND 5253 } 5254 } 5255 if (!packed_loaded || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) { 5256 |4: 5257 if (!op2_loaded) { 5258 | // hval = Z_LVAL_P(dim); 5259 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 5260 } 5261 if (packed_loaded) { 5262 | EXT_CALL _zend_hash_index_find, REG0 5263 } else { 5264 | EXT_CALL zend_hash_index_find, REG0 5265 } 5266 | mov REG0, RETVALx 5267 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { 5268 | cbz REG0, &exit_addr 5269 } else if (type == BP_VAR_IS && not_found_exit_addr) { 5270 | cbz REG0, ¬_found_exit_addr 5271 } else if (type == BP_VAR_IS && found_exit_addr) { 5272 | cbz REG0, >7 // NOT_FOUND 5273 } else { 5274 | cbz REG0, >2 // NOT_FOUND 5275 } 5276 } 5277 |.cold_code 5278 |2: 5279 switch (type) { 5280 case BP_VAR_R: 5281 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) { 5282 | // zend_error(E_WARNING,"Undefined array key " ZEND_LONG_FMT, hval); 5283 | // retval = &EG(uninitialized_zval); 5284 | UNDEFINED_OFFSET opline 5285 | b >9 5286 } 5287 break; 5288 case BP_VAR_IS: 5289 case BP_VAR_UNSET: 5290 if (!not_found_exit_addr && !found_exit_addr) { 5291 | // retval = &EG(uninitialized_zval); 5292 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 5293 | b >9 5294 } 5295 break; 5296 default: 5297 ZEND_UNREACHABLE(); 5298 } 5299 |.code 5300 break; 5301 case BP_VAR_RW: 5302 if (packed_loaded && !not_found_exit_addr) { 5303 | IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w 5304 } 5305 if (!packed_loaded || 5306 !not_found_exit_addr || 5307 (op1_info & MAY_BE_ARRAY_NUMERIC_HASH)) { 5308 if (packed_loaded && not_found_exit_addr) { 5309 |.cold_code 5310 } 5311 |2: 5312 |4: 5313 if (!op2_loaded) { 5314 | // hval = Z_LVAL_P(dim); 5315 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 5316 } 5317 if (packed_loaded) { 5318 | EXT_CALL zend_jit_hash_index_lookup_rw_no_packed, REG0 5319 } else { 5320 | EXT_CALL zend_jit_hash_index_lookup_rw, REG0 5321 } 5322 | mov REG0, RETVALx 5323 if (not_found_exit_addr) { 5324 if (packed_loaded) { 5325 | cbnz REG0, >8 5326 | b ¬_found_exit_addr 5327 |.code 5328 } else { 5329 | cbz REG0, ¬_found_exit_addr 5330 } 5331 } else { 5332 | cbz REG0, >9 5333 } 5334 } 5335 break; 5336 case BP_VAR_W: 5337 if (packed_loaded) { 5338 | IF_NOT_Z_TYPE REG0, IS_UNDEF, >8, TMP1w 5339 } 5340 if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || (op1_info & MAY_BE_ARRAY_NUMERIC_HASH) || packed_loaded || bad_packed_key || dim_type == IS_UNDEF) { 5341 |2: 5342 |4: 5343 if (!op2_loaded) { 5344 | // hval = Z_LVAL_P(dim); 5345 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 5346 } 5347 | EXT_CALL zend_hash_index_lookup, REG0 5348 | mov REG0, RETVALx 5349 } 5350 break; 5351 default: 5352 ZEND_UNREACHABLE(); 5353 } 5354 5355 if (type != BP_JIT_IS && (op2_info & MAY_BE_STRING)) { 5356 | b >8 5357 } 5358 } 5359 5360 if (op2_info & MAY_BE_STRING) { 5361 |3: 5362 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) { 5363 | // if (EXPECTED(Z_TYPE_P(dim) == IS_STRING)) 5364 | IF_NOT_ZVAL_TYPE op2_addr, IS_STRING, >3, ZREG_TMP1 5365 } 5366 | // offset_key = Z_STR_P(dim); 5367 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 5368 | // retval = zend_hash_find(ht, offset_key); 5369 switch (type) { 5370 case BP_JIT_IS: 5371 if (opline->op2_type != IS_CONST) { 5372 | ldrb TMP1w, [FCARG2x, #offsetof(zend_string, val)] 5373 | cmp TMP1w, #((uint8_t) ('9')) 5374 | ble >1 5375 |.cold_code 5376 |1: 5377 | EXT_CALL zend_jit_symtable_find, REG0 5378 | b >1 5379 |.code 5380 | EXT_CALL zend_hash_find, REG0 5381 |1: 5382 } else { 5383 | EXT_CALL zend_hash_find_known_hash, REG0 5384 } 5385 | mov REG0, RETVALx 5386 if (not_found_exit_addr) { 5387 | cbz REG0, ¬_found_exit_addr 5388 } else { 5389 | cbz REG0, >9 // NOT_FOUND 5390 } 5391 break; 5392 case BP_VAR_R: 5393 case BP_VAR_IS: 5394 case BP_VAR_UNSET: 5395 if (opline->op2_type != IS_CONST) { 5396 | ldrb TMP1w, [FCARG2x, #offsetof(zend_string, val)] 5397 | cmp TMP1w, #((uint8_t) ('9')) 5398 | ble >1 5399 |.cold_code 5400 |1: 5401 | EXT_CALL zend_jit_symtable_find, REG0 5402 | b >1 5403 |.code 5404 | EXT_CALL zend_hash_find, REG0 5405 |1: 5406 } else { 5407 | EXT_CALL zend_hash_find_known_hash, REG0 5408 } 5409 | mov REG0, RETVALx 5410 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { 5411 | cbz REG0, &exit_addr 5412 } else if (type == BP_VAR_IS && not_found_exit_addr) { 5413 | cbz REG0, ¬_found_exit_addr 5414 } else if (type == BP_VAR_IS && found_exit_addr) { 5415 | cbz REG0, >7 5416 } else { 5417 | cbz REG0, >2 // NOT_FOUND 5418 |.cold_code 5419 |2: 5420 switch (type) { 5421 case BP_VAR_R: 5422 // zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key)); 5423 | UNDEFINED_INDEX opline 5424 | b >9 5425 break; 5426 case BP_VAR_IS: 5427 case BP_VAR_UNSET: 5428 | // retval = &EG(uninitialized_zval); 5429 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 5430 | b >9 5431 break; 5432 default: 5433 ZEND_UNREACHABLE(); 5434 } 5435 |.code 5436 } 5437 break; 5438 case BP_VAR_RW: 5439 if (opline->op2_type != IS_CONST) { 5440 | EXT_CALL zend_jit_symtable_lookup_rw, REG0 5441 } else { 5442 | EXT_CALL zend_jit_hash_lookup_rw, REG0 5443 } 5444 | mov REG0, RETVALx 5445 if (not_found_exit_addr) { 5446 | cbz REG0, ¬_found_exit_addr 5447 } else { 5448 | cbz REG0, >9 5449 } 5450 break; 5451 case BP_VAR_W: 5452 if (opline->op2_type != IS_CONST) { 5453 | EXT_CALL zend_jit_symtable_lookup_w, REG0 5454 } else { 5455 | EXT_CALL zend_hash_lookup, REG0 5456 } 5457 | mov REG0, RETVALx 5458 break; 5459 default: 5460 ZEND_UNREACHABLE(); 5461 } 5462 } 5463 5464 if (type == BP_JIT_IS && (op2_info & (MAY_BE_LONG|MAY_BE_STRING))) { 5465 |5: 5466 if (op1_info & MAY_BE_ARRAY_OF_REF) { 5467 | ZVAL_DEREF REG0, MAY_BE_REF, TMP1w 5468 } 5469 | ldrb TMP1w, [REG0,#offsetof(zval, u1.v.type)] 5470 | cmp TMP1w, #IS_NULL 5471 if (not_found_exit_addr) { 5472 | ble ¬_found_exit_addr 5473 } else if (found_exit_addr) { 5474 | bgt &found_exit_addr 5475 } else { 5476 | ble >9 // NOT FOUND 5477 } 5478 } 5479 5480 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) { 5481 if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) { 5482 |.cold_code 5483 |3: 5484 } 5485 if (type != BP_VAR_RW) { 5486 | SET_EX_OPLINE opline, REG0 5487 } 5488 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 5489 switch (type) { 5490 case BP_VAR_R: 5491 | LOAD_ZVAL_ADDR CARG3, res_addr 5492 | EXT_CALL zend_jit_fetch_dim_r_helper, REG0 5493 | mov REG0, RETVALx 5494 | b >9 5495 break; 5496 case BP_JIT_IS: 5497 | EXT_CALL zend_jit_fetch_dim_isset_helper, REG0 5498 | mov REG0, RETVALx 5499 if (not_found_exit_addr) { 5500 | cbz REG0, ¬_found_exit_addr 5501 if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) { 5502 | b >8 5503 } 5504 } else if (found_exit_addr) { 5505 | cbnz REG0, &found_exit_addr 5506 if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) { 5507 | b >9 5508 } 5509 } else { 5510 | cbnz REG0, >8 5511 | b >9 5512 } 5513 break; 5514 case BP_VAR_IS: 5515 case BP_VAR_UNSET: 5516 | LOAD_ZVAL_ADDR CARG3, res_addr 5517 | EXT_CALL zend_jit_fetch_dim_is_helper, REG0 5518 | mov REG0, RETVALx 5519 | b >9 5520 break; 5521 case BP_VAR_RW: 5522 | EXT_CALL zend_jit_fetch_dim_rw_helper, REG0 5523 | mov REG0, RETVALx 5524 | cbnz REG0, >8 5525 | b >9 5526 break; 5527 case BP_VAR_W: 5528 | EXT_CALL zend_jit_fetch_dim_w_helper, REG0 5529 | mov REG0, RETVALx 5530 | cbnz REG0, >8 5531 | b >9 5532 break; 5533 default: 5534 ZEND_UNREACHABLE(); 5535 } 5536 if (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) { 5537 |.code 5538 } 5539 } 5540 5541 return 1; 5542} 5543 5544static int zend_jit_simple_assign(dasm_State **Dst, 5545 const zend_op *opline, 5546 zend_jit_addr var_addr, 5547 uint32_t var_info, 5548 uint32_t var_def_info, 5549 zend_uchar val_type, 5550 zend_jit_addr val_addr, 5551 uint32_t val_info, 5552 zend_jit_addr res_addr, 5553 int in_cold, 5554 int save_r1, 5555 bool check_exception) 5556/* Labels: 1,2,3 */ 5557{ 5558 zend_reg tmp_reg; 5559 5560 if (Z_MODE(var_addr) == IS_REG || Z_REG(var_addr) != ZREG_REG0) { 5561 tmp_reg = ZREG_REG0; 5562 } else { 5563 /* ASSIGN_DIM */ 5564 tmp_reg = ZREG_FCARG1; 5565 } 5566 5567 if (Z_MODE(val_addr) == IS_CONST_ZVAL) { 5568 zval *zv = Z_ZV(val_addr); 5569 5570 if (!res_addr) { 5571 | ZVAL_COPY_CONST var_addr, var_info, var_def_info, zv, tmp_reg, ZREG_TMP1, ZREG_FPR0 5572 } else { 5573 | ZVAL_COPY_CONST_2 var_addr, res_addr, var_info, var_def_info, zv, tmp_reg, ZREG_TMP1, ZREG_FPR0 5574 } 5575 if (Z_REFCOUNTED_P(zv)) { 5576 if (!res_addr) { 5577 | ADDREF_CONST zv, TMP1, TMP2 5578 } else { 5579 | ADDREF_CONST_2 zv, TMP1, TMP2 5580 } 5581 } 5582 } else { 5583 if (val_info & MAY_BE_UNDEF) { 5584 if (in_cold) { 5585 | IF_NOT_ZVAL_TYPE val_addr, IS_UNDEF, >2, ZREG_TMP1 5586 } else { 5587 | IF_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1 5588 |.cold_code 5589 |1: 5590 } 5591 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 5592 if (save_r1) { 5593 | str FCARG1x, T1 // save 5594 } 5595 | SET_ZVAL_TYPE_INFO var_addr, IS_NULL, TMP1w, TMP2 5596 if (res_addr) { 5597 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 5598 } 5599 if (opline) { 5600 | SET_EX_OPLINE opline, Rx(tmp_reg) 5601 } 5602 ZEND_ASSERT(Z_MODE(val_addr) == IS_MEM_ZVAL && Z_REG(val_addr) == ZREG_FP); 5603 | LOAD_32BIT_VAL FCARG1w, Z_OFFSET(val_addr) 5604 | EXT_CALL zend_jit_undefined_op_helper, REG0 5605 if (check_exception) { 5606 | cbz RETVALx, ->exception_handler_undef 5607 } 5608 if (save_r1) { 5609 | ldr FCARG1x, T1 // restore 5610 } 5611 | b >3 5612 if (in_cold) { 5613 |2: 5614 } else { 5615 |.code 5616 } 5617 } 5618 if (val_info & MAY_BE_REF) { 5619 if (val_type == IS_CV) { 5620 ZEND_ASSERT(Z_REG(var_addr) != ZREG_REG2); 5621 if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_REG2 || Z_OFFSET(val_addr) != 0) { 5622 | LOAD_ZVAL_ADDR REG2, val_addr 5623 } 5624 | ZVAL_DEREF REG2, val_info, TMP1w 5625 val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, 0); 5626 } else { 5627 zend_jit_addr ref_addr; 5628 5629 if (in_cold) { 5630 | IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, >1, ZREG_TMP1 5631 } else { 5632 | IF_ZVAL_TYPE val_addr, IS_REFERENCE, >1, ZREG_TMP1 5633 |.cold_code 5634 |1: 5635 } 5636 if (Z_REG(val_addr) == ZREG_REG2) { 5637 | str REG2, T1 // save 5638 } 5639 | // zend_refcounted *ref = Z_COUNTED_P(retval_ptr); 5640 | GET_ZVAL_PTR REG2, val_addr, TMP1 5641 | GC_DELREF REG2, TMP1w 5642 | // ZVAL_COPY_VALUE(return_value, &ref->val); 5643 ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, offsetof(zend_reference, val)); 5644 if (!res_addr) { 5645 | ZVAL_COPY_VALUE var_addr, var_info, ref_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 5646 } else { 5647 | ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, ref_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 5648 } 5649 | beq >2 // GC_DELREF() reached zero 5650 | IF_NOT_REFCOUNTED REG2w, >3, TMP1w 5651 if (!res_addr) { 5652 | GC_ADDREF Rx(tmp_reg), TMP1w 5653 } else { 5654 | GC_ADDREF_2 Rx(tmp_reg), TMP1w 5655 } 5656 | b >3 5657 |2: 5658 if (res_addr) { 5659 | IF_NOT_REFCOUNTED REG2w, >2, TMP1w 5660 | GC_ADDREF Rx(tmp_reg), TMP1w 5661 |2: 5662 } 5663 if (Z_REG(val_addr) == ZREG_REG2) { 5664 | ldr REG2, T1 // restore 5665 } 5666 if (save_r1) { 5667 | str FCARG1x, T1 // save 5668 } 5669 | GET_ZVAL_PTR FCARG1x, val_addr, TMP1 5670 | EFREE_REFERENCE 5671 if (save_r1) { 5672 | ldr FCARG1x, T1 // restore 5673 } 5674 | b >3 5675 if (in_cold) { 5676 |1: 5677 } else { 5678 |.code 5679 } 5680 } 5681 } 5682 5683 if (!res_addr) { 5684 | ZVAL_COPY_VALUE var_addr, var_info, val_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 5685 } else { 5686 | ZVAL_COPY_VALUE_2 var_addr, var_info, res_addr, val_addr, val_info, ZREG_REG2, tmp_reg, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 5687 } 5688 5689 if (val_type == IS_CV) { 5690 if (!res_addr) { 5691 | TRY_ADDREF val_info, REG2w, Rx(tmp_reg), TMP1w 5692 } else { 5693 | TRY_ADDREF_2 val_info, REG2w, Rx(tmp_reg), TMP1w 5694 } 5695 } else { 5696 if (res_addr) { 5697 | TRY_ADDREF val_info, REG2w, Rx(tmp_reg), TMP1w 5698 } 5699 } 5700 |3: 5701 } 5702 return 1; 5703} 5704 5705static int zend_jit_assign_to_typed_ref(dasm_State **Dst, 5706 const zend_op *opline, 5707 zend_uchar val_type, 5708 zend_jit_addr val_addr, 5709 zend_jit_addr res_addr, 5710 bool check_exception) 5711{ 5712 | // if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) { 5713 | ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)] 5714 | cbnz TMP1, >2 5715 |.cold_code 5716 |2: 5717 if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) { 5718 | LOAD_ZVAL_ADDR FCARG2x, val_addr 5719 } 5720 if (opline) { 5721 | SET_EX_OPLINE opline, REG0 5722 } 5723 if (val_type == IS_CONST) { 5724 | EXT_CALL zend_jit_assign_const_to_typed_ref, REG0 5725 } else if (val_type == IS_TMP_VAR) { 5726 | EXT_CALL zend_jit_assign_tmp_to_typed_ref, REG0 5727 } else if (val_type == IS_VAR) { 5728 | EXT_CALL zend_jit_assign_var_to_typed_ref, REG0 5729 } else if (val_type == IS_CV) { 5730 | EXT_CALL zend_jit_assign_cv_to_typed_ref, REG0 5731 } else { 5732 ZEND_UNREACHABLE(); 5733 } 5734 if (res_addr) { 5735 zend_jit_addr ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_X0, 0); // RETVAL 5736 5737 | ZVAL_COPY_VALUE res_addr, -1, ret_addr, -1, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 5738 | TRY_ADDREF -1, REG1w, REG2, TMP1w 5739 } 5740 if (check_exception) { 5741 | // if (UNEXPECTED(EG(exception) != NULL)) { 5742 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 5743 | cbz REG0, >8 // END OF zend_jit_assign_to_variable() 5744 | b ->exception_handler 5745 } else { 5746 | b >8 5747 } 5748 |.code 5749 5750 return 1; 5751} 5752 5753static int zend_jit_assign_to_variable_call(dasm_State **Dst, 5754 const zend_op *opline, 5755 zend_jit_addr __var_use_addr, 5756 zend_jit_addr var_addr, 5757 uint32_t __var_info, 5758 uint32_t __var_def_info, 5759 zend_uchar val_type, 5760 zend_jit_addr val_addr, 5761 uint32_t val_info, 5762 zend_jit_addr __res_addr, 5763 bool __check_exception) 5764{ 5765 if (val_info & MAY_BE_UNDEF) { 5766 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 5767 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 5768 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 5769 5770 if (!exit_addr) { 5771 return 0; 5772 } 5773 5774 | IF_ZVAL_TYPE val_addr, IS_UNDEF, &exit_addr, ZREG_TMP1 5775 } else { 5776 | IF_ZVAL_TYPE val_addr, IS_UNDEF, >1, ZREG_TMP1 5777 |.cold_code 5778 |1: 5779 ZEND_ASSERT(Z_REG(val_addr) == ZREG_FP); 5780 if (Z_REG(var_addr) != ZREG_FP) { 5781 | str Rx(Z_REG(var_addr)), T1 // save 5782 } 5783 | SET_EX_OPLINE opline, REG0 5784 | LOAD_32BIT_VAL FCARG1w, Z_OFFSET(val_addr) 5785 | EXT_CALL zend_jit_undefined_op_helper, REG0 5786 if (Z_REG(var_addr) != ZREG_FP) { 5787 | ldr Rx(Z_REG(var_addr)), T1 // restore 5788 } 5789 if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) { 5790 | LOAD_ZVAL_ADDR FCARG1x, var_addr 5791 } 5792 | LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval 5793 | bl ->assign_const 5794 | b >9 5795 |.code 5796 |1: 5797 } 5798 } 5799 if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) { 5800 | LOAD_ZVAL_ADDR FCARG1x, var_addr 5801 } 5802 if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) { 5803 | LOAD_ZVAL_ADDR FCARG2x, val_addr 5804 } 5805 if (opline) { 5806 | SET_EX_OPLINE opline, REG0 5807 } 5808 if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { 5809 | bl ->assign_tmp 5810 } else if (val_type == IS_CONST) { 5811 | bl ->assign_const 5812 } else if (val_type == IS_TMP_VAR) { 5813 | bl ->assign_tmp 5814 } else if (val_type == IS_VAR) { 5815 if (!(val_info & MAY_BE_REF)) { 5816 | bl ->assign_tmp 5817 } else { 5818 | bl ->assign_var 5819 } 5820 } else if (val_type == IS_CV) { 5821 if (!(val_info & MAY_BE_REF)) { 5822 | bl ->assign_cv_noref 5823 } else { 5824 | bl ->assign_cv 5825 } 5826 if ((val_info & MAY_BE_UNDEF) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) { 5827 |9: 5828 } 5829 } else { 5830 ZEND_UNREACHABLE(); 5831 } 5832 5833 return 1; 5834} 5835 5836static int zend_jit_assign_to_variable(dasm_State **Dst, 5837 const zend_op *opline, 5838 zend_jit_addr var_use_addr, 5839 zend_jit_addr var_addr, 5840 uint32_t var_info, 5841 uint32_t var_def_info, 5842 zend_uchar val_type, 5843 zend_jit_addr val_addr, 5844 uint32_t val_info, 5845 zend_jit_addr res_addr, 5846 bool check_exception) 5847/* Labels: 1,2,3,4,5,8 */ 5848{ 5849 int done = 0; 5850 zend_reg ref_reg, tmp_reg; 5851 5852 if (Z_MODE(var_addr) == IS_REG || Z_REG(var_use_addr) != ZREG_REG0) { 5853 ref_reg = ZREG_FCARG1; 5854 tmp_reg = ZREG_REG0; 5855 } else { 5856 /* ASSIGN_DIM */ 5857 ref_reg = ZREG_REG0; 5858 tmp_reg = ZREG_FCARG1; 5859 } 5860 5861 if (var_info & MAY_BE_REF) { 5862 if (Z_MODE(var_use_addr) != IS_MEM_ZVAL || Z_REG(var_use_addr) != ref_reg || Z_OFFSET(var_use_addr) != 0) { 5863 | LOAD_ZVAL_ADDR Rx(ref_reg), var_use_addr 5864 var_addr = var_use_addr = ZEND_ADDR_MEM_ZVAL(ref_reg, 0); 5865 } 5866 | // if (Z_ISREF_P(variable_ptr)) { 5867 | IF_NOT_Z_TYPE Rx(ref_reg), IS_REFERENCE, >3, TMP1w 5868 | // if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) { 5869 | GET_Z_PTR FCARG1x, Rx(ref_reg) 5870 if (!zend_jit_assign_to_typed_ref(Dst, opline, val_type, val_addr, res_addr, check_exception)) { 5871 return 0; 5872 } 5873 | add Rx(ref_reg), FCARG1x, #offsetof(zend_reference, val) 5874 |3: 5875 } 5876 if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 5877 if (RC_MAY_BE_1(var_info)) { 5878 int in_cold = 0; 5879 5880 if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 5881 | IF_ZVAL_REFCOUNTED var_use_addr, >1, ZREG_TMP1, ZREG_TMP2 5882 |.cold_code 5883 |1: 5884 in_cold = 1; 5885 } 5886 if (Z_REG(var_use_addr) == ZREG_FCARG1 || Z_REG(var_use_addr) == ZREG_REG0) { 5887 bool keep_gc = 0; 5888 5889 | GET_ZVAL_PTR Rx(tmp_reg), var_use_addr, TMP1 5890#if 0 5891 // TODO: This optiization doesn't work on ARM 5892 if (tmp_reg == ZREG_FCARG1) { 5893 if (Z_MODE(val_addr) == IS_REG) { 5894 keep_gc = 1; 5895 } else if ((val_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) == 0) { 5896 keep_gc = 1; 5897 } else if (Z_MODE(val_addr) == IS_CONST_ZVAL) { 5898 zval *zv = Z_ZV(val_addr); 5899 if (Z_TYPE_P(zv) == IS_DOUBLE) { 5900 if (Z_DVAL_P(zv) == 0) { 5901 keep_gc = 1; 5902 } 5903 } else if (IS_SIGNED_32BIT(Z_LVAL_P(zv))) { 5904 keep_gc = 1; 5905 } 5906 } else if (Z_MODE(val_addr) == IS_MEM_ZVAL) { 5907 if ((val_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_GUARD)) == MAY_BE_DOUBLE) { 5908 keep_gc = 1; 5909 } 5910 } 5911 } 5912#endif 5913 if (!keep_gc) { 5914 | str Rx(tmp_reg), T1 // save 5915 } 5916 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)) { 5917 return 0; 5918 } 5919 if (!keep_gc) { 5920 | ldr FCARG1x, T1 // restore 5921 } 5922 } else { 5923 | GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1 5924 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)) { 5925 return 0; 5926 } 5927 } 5928 | GC_DELREF FCARG1x, TMP1w 5929 if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) { 5930 | bne >4 5931 } else { 5932 | bne >8 5933 } 5934 | ZVAL_DTOR_FUNC var_info, opline, TMP1 5935 if (in_cold || (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0)) { 5936 if (check_exception && !(val_info & MAY_BE_UNDEF)) { 5937 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 5938 | cbz REG0, >8 5939 | b ->exception_handler 5940 } else { 5941 | b >8 5942 } 5943 } 5944 if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) { 5945 |4: 5946 | IF_GC_MAY_NOT_LEAK FCARG1x, >8, TMP1w, TMP2w 5947 | EXT_CALL gc_possible_root, REG0 5948 if (in_cold) { 5949 | b >8 5950 } 5951 } 5952 if (check_exception && (val_info & MAY_BE_UNDEF)) { 5953 |8: 5954 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 5955 | cbz REG0, >8 5956 | b ->exception_handler 5957 } 5958 if (in_cold) { 5959 |.code 5960 } else { 5961 done = 1; 5962 } 5963 } else /* if (RC_MAY_BE_N(var_info)) */ { 5964 if (var_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 5965 | IF_NOT_ZVAL_REFCOUNTED var_use_addr, >5, ZREG_TMP1, ZREG_TMP2 5966 } 5967 if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) { 5968 if (Z_REG(var_use_addr) != ZREG_FP) { 5969 | str Rx(Z_REG(var_use_addr)), T1 // save 5970 } 5971 | GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1 5972 | GC_DELREF FCARG1x, TMP1w 5973 | IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w 5974 | EXT_CALL gc_possible_root, TMP1 5975 if (Z_REG(var_use_addr) != ZREG_FP) { 5976 | ldr Rx(Z_REG(var_use_addr)), T1 // restore 5977 } 5978 } else { 5979 | GET_ZVAL_PTR Rx(tmp_reg), var_use_addr, TMP1 5980 | GC_DELREF Rx(tmp_reg), TMP1w 5981 } 5982 |5: 5983 } 5984 } 5985 5986 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)) { 5987 return 0; 5988 } 5989 5990 |8: 5991 5992 return 1; 5993} 5994 5995static 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) 5996{ 5997 zend_jit_addr op2_addr, op3_addr, res_addr; 5998 5999 op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0; 6000 op3_addr = OP1_DATA_ADDR(); 6001 if (opline->result_type == IS_UNUSED) { 6002 res_addr = 0; 6003 } else { 6004 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 6005 } 6006 6007 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (val_info & MAY_BE_UNDEF)) { 6008 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 6009 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 6010 6011 if (!exit_addr) { 6012 return 0; 6013 } 6014 6015 | IF_ZVAL_TYPE op3_addr, IS_UNDEF, &exit_addr, ZREG_TMP1 6016 6017 val_info &= ~MAY_BE_UNDEF; 6018 } 6019 6020 if (op1_info & MAY_BE_REF) { 6021 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 6022 | IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w 6023 | GET_Z_PTR FCARG2x, FCARG1x 6024 | ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))] 6025 | cmp TMP1w, #IS_ARRAY 6026 | bne >2 6027 | add FCARG1x, FCARG2x, #offsetof(zend_reference, val) 6028 | b >3 6029 |.cold_code 6030 |2: 6031 | SET_EX_OPLINE opline, REG0 6032 | EXT_CALL zend_jit_prepare_assign_dim_ref, REG0 6033 | mov FCARG1x, RETVALx 6034 | cbnz FCARG1x, >1 6035 | b ->exception_handler_undef 6036 |.code 6037 |1: 6038 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 6039 } 6040 6041 if (op1_info & MAY_BE_ARRAY) { 6042 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) { 6043 | IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1 6044 } 6045 |3: 6046 | SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2 6047 } else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) { 6048 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 6049 | CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1 6050 | bgt >7 6051 } 6052 | // ZVAL_ARR(container, zend_new_array(8)); 6053 if (Z_REG(op1_addr) != ZREG_FP) { 6054 | str Rx(Z_REG(op1_addr)), T1 // save 6055 } 6056 | EXT_CALL _zend_new_array_0, REG0 6057 | mov REG0, RETVALx 6058 if (Z_REG(op1_addr) != ZREG_FP) { 6059 | ldr Rx(Z_REG(op1_addr)), T1 // restore 6060 } 6061 | SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1 6062 | SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2 6063 | mov FCARG1x, REG0 6064 } 6065 6066 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 6067 |6: 6068 if (opline->op2_type == IS_UNUSED) { 6069 uint32_t var_info = MAY_BE_NULL; 6070 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 6071 6072 | // var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); 6073 | LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval 6074 | EXT_CALL zend_hash_next_index_insert, REG0 6075 | // if (UNEXPECTED(!var_ptr)) { 6076 | mov REG0, RETVALx 6077 | cbz REG0, >1 6078 |.cold_code 6079 |1: 6080 | // zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied"); 6081 | CANNOT_ADD_ELEMENT opline 6082 | //ZEND_VM_C_GOTO(assign_dim_op_ret_null); 6083 | b >9 6084 |.code 6085 6086 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)) { 6087 return 0; 6088 } 6089 } else { 6090 uint32_t var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0); 6091 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 6092 6093 if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_W, op1_info, op2_info, dim_type, NULL, NULL, NULL)) { 6094 return 0; 6095 } 6096 6097 if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) { 6098 var_info |= MAY_BE_REF; 6099 } 6100 if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 6101 var_info |= MAY_BE_RC1; 6102 } 6103 6104 |8: 6105 | // value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE); 6106 if (opline->op1_type == IS_VAR) { 6107 ZEND_ASSERT(opline->result_type == IS_UNUSED); 6108 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)) { 6109 return 0; 6110 } 6111 } else { 6112 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)) { 6113 return 0; 6114 } 6115 } 6116 } 6117 } 6118 6119 if (((op1_info & MAY_BE_ARRAY) && 6120 (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) || 6121 (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY)))) { 6122 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 6123 |.cold_code 6124 |7: 6125 } 6126 6127 if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) && 6128 (op1_info & MAY_BE_ARRAY)) { 6129 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 6130 | CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1 6131 | bgt >2 6132 } 6133 | // ZVAL_ARR(container, zend_new_array(8)); 6134 if (Z_REG(op1_addr) != ZREG_FP) { 6135 | str Rx(Z_REG(op1_addr)), T1 // save 6136 } 6137 | EXT_CALL _zend_new_array_0, REG0 6138 | mov REG0, RETVALx 6139 if (Z_REG(op1_addr) != ZREG_FP) { 6140 | ldr Rx(Z_REG(op1_addr)), T1 // restore 6141 } 6142 | SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1 6143 | SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2 6144 | mov FCARG1x, REG0 6145 | // ZEND_VM_C_GOTO(assign_dim_op_new_array); 6146 | b <6 6147 |2: 6148 } 6149 6150 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 6151 | SET_EX_OPLINE opline, REG0 6152 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 6153 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 6154 } 6155 if (opline->op2_type == IS_UNUSED) { 6156 | mov FCARG2x, xzr 6157 } else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) { 6158 ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL); 6159 | LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1) 6160 } else { 6161 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 6162 } 6163 if (opline->result_type == IS_UNUSED) { 6164 | mov CARG4, xzr 6165 } else { 6166 | LOAD_ZVAL_ADDR CARG4, res_addr 6167 } 6168 | LOAD_ZVAL_ADDR CARG3, op3_addr 6169 | EXT_CALL zend_jit_assign_dim_helper, REG0 6170 6171#ifdef ZEND_JIT_USE_RC_INFERENCE 6172 if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR)) && (val_info & MAY_BE_RC1)) { 6173 /* ASSIGN_DIM may increase refcount of the value */ 6174 val_info |= MAY_BE_RCN; 6175 } 6176#endif 6177 6178 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 6179 } 6180 6181 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 6182 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 6183 | b >9 // END 6184 } 6185 |.code 6186 } 6187 } 6188 6189#ifdef ZEND_JIT_USE_RC_INFERENCE 6190 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))) { 6191 /* ASSIGN_DIM may increase refcount of the key */ 6192 op2_info |= MAY_BE_RCN; 6193 } 6194#endif 6195 6196 |9: 6197 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 6198 6199 if (may_throw) { 6200 zend_jit_check_exception(Dst); 6201 } 6202 6203 return 1; 6204} 6205 6206static 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) 6207{ 6208 zend_jit_addr op2_addr, op3_addr, var_addr; 6209 const void *not_found_exit_addr = NULL; 6210 uint32_t var_info = MAY_BE_NULL; 6211 6212 ZEND_ASSERT(opline->result_type == IS_UNUSED); 6213 6214 op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0; 6215 op3_addr = OP1_DATA_ADDR(); 6216 6217 | SET_EX_OPLINE opline, REG0 6218 if (op1_info & MAY_BE_REF) { 6219 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 6220 | IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w 6221 | GET_Z_PTR FCARG2x, FCARG1x 6222 | ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))] 6223 | cmp TMP1w, #IS_ARRAY 6224 | bne >2 6225 | add FCARG1x, FCARG2x, #offsetof(zend_reference, val) 6226 | b >3 6227 |.cold_code 6228 |2: 6229 | EXT_CALL zend_jit_prepare_assign_dim_ref, REG0 6230 | mov FCARG1x, RETVALx 6231 | cbnz RETVALx, >1 6232 | b ->exception_handler_undef 6233 |.code 6234 |1: 6235 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 6236 } 6237 6238 if (op1_info & MAY_BE_ARRAY) { 6239 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) { 6240 | IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1 6241 } 6242 |3: 6243 | SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2 6244 } 6245 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) { 6246 if (op1_info & MAY_BE_ARRAY) { 6247 |.cold_code 6248 |7: 6249 } 6250 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 6251 | CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1 6252 | bgt >7 6253 } 6254 if (Z_REG(op1_addr) != ZREG_FP) { 6255 | str Rx(Z_REG(op1_addr)), T1 // save 6256 } 6257 if (op1_info & MAY_BE_UNDEF) { 6258 if (op1_info & MAY_BE_NULL) { 6259 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 6260 } 6261 | LOAD_32BIT_VAL FCARG1x, opline->op1.var 6262 | EXT_CALL zend_jit_undefined_op_helper, REG0 6263 |1: 6264 } 6265 | // ZVAL_ARR(container, zend_new_array(8)); 6266 | EXT_CALL _zend_new_array_0, REG0 6267 | mov REG0, RETVALx 6268 if (Z_REG(op1_addr) != ZREG_FP) { 6269 | ldr Rx(Z_REG(op1_addr)), T1 // restore 6270 } 6271 | SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1 6272 | SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2 6273 | mov FCARG1x, REG0 6274 if (op1_info & MAY_BE_ARRAY) { 6275 | b >1 6276 |.code 6277 |1: 6278 } 6279 } 6280 6281 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 6282 uint32_t var_def_info = zend_array_element_type(op1_def_info, opline->op1_type, 1, 0); 6283 6284 |6: 6285 if (opline->op2_type == IS_UNUSED) { 6286 var_info = MAY_BE_NULL; 6287 6288 | // var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); 6289 | LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval 6290 | EXT_CALL zend_hash_next_index_insert, REG0 6291 | mov REG0, RETVALx 6292 | // if (UNEXPECTED(!var_ptr)) { 6293 | cbz REG0, >1 6294 |.cold_code 6295 |1: 6296 | // zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied"); 6297 | CANNOT_ADD_ELEMENT opline 6298 | //ZEND_VM_C_GOTO(assign_dim_op_ret_null); 6299 | b >9 6300 |.code 6301 } else { 6302 var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0); 6303 if (op1_info & (MAY_BE_ARRAY_OF_REF|MAY_BE_OBJECT)) { 6304 var_info |= MAY_BE_REF; 6305 } 6306 if (var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 6307 var_info |= MAY_BE_RC1; 6308 } 6309 6310 if (dim_type != IS_UNKNOWN 6311 && dim_type != IS_UNDEF 6312 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY 6313 && (op2_info & (MAY_BE_LONG|MAY_BE_STRING)) 6314 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) { 6315 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 6316 not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point); 6317 if (!not_found_exit_addr) { 6318 return 0; 6319 } 6320 } 6321 6322 if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, dim_type, NULL, not_found_exit_addr, NULL)) { 6323 return 0; 6324 } 6325 6326 |8: 6327 if (not_found_exit_addr && dim_type != IS_REFERENCE) { 6328 | IF_NOT_Z_TYPE, REG0, dim_type, ¬_found_exit_addr, TMP1w 6329 var_info = (1 << dim_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)); 6330 } 6331 if (var_info & MAY_BE_REF) { 6332 binary_op_type binary_op = get_binary_op(opline->extended_value); 6333 | IF_NOT_Z_TYPE, REG0, IS_REFERENCE, >1, TMP1w 6334 | GET_Z_PTR FCARG1x, REG0 6335 | ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)] 6336 | cbnz TMP1, >2 6337 | add REG0, FCARG1x, #offsetof(zend_reference, val) 6338 |.cold_code 6339 |2: 6340 | LOAD_ZVAL_ADDR FCARG2x, op3_addr 6341 | LOAD_ADDR CARG3, binary_op 6342 if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR)) 6343 && (op1_data_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 6344 | EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0 6345 } else { 6346 | EXT_CALL zend_jit_assign_op_to_typed_ref, REG0 6347 } 6348 | b >9 6349 |.code 6350 |1: 6351 } 6352 } 6353 6354 var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 6355 switch (opline->extended_value) { 6356 case ZEND_ADD: 6357 case ZEND_SUB: 6358 case ZEND_MUL: 6359 case ZEND_DIV: 6360 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, 6361 1 /* may overflow */, may_throw)) { 6362 return 0; 6363 } 6364 break; 6365 case ZEND_BW_OR: 6366 case ZEND_BW_AND: 6367 case ZEND_BW_XOR: 6368 case ZEND_SL: 6369 case ZEND_SR: 6370 case ZEND_MOD: 6371 if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value, 6372 IS_CV, opline->op1, var_addr, var_info, NULL, 6373 (opline+1)->op1_type, (opline+1)->op1, op3_addr, op1_data_info, 6374 op1_data_range, 6375 0, var_addr, var_def_info, var_info, may_throw)) { 6376 return 0; 6377 } 6378 break; 6379 case ZEND_CONCAT: 6380 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, 6381 may_throw)) { 6382 return 0; 6383 } 6384 break; 6385 default: 6386 ZEND_UNREACHABLE(); 6387 } 6388 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 6389 } 6390 6391 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 6392 binary_op_type binary_op; 6393 6394 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 6395 |.cold_code 6396 |7: 6397 } 6398 6399 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 6400 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 6401 } 6402 if (opline->op2_type == IS_UNUSED) { 6403 | mov FCARG2x, xzr 6404 } else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) { 6405 ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL); 6406 | LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1) 6407 } else { 6408 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 6409 } 6410 binary_op = get_binary_op(opline->extended_value); 6411 | LOAD_ZVAL_ADDR CARG3, op3_addr 6412 | LOAD_ADDR CARG4, binary_op 6413 | EXT_CALL zend_jit_assign_dim_op_helper, REG0 6414 6415 |9: 6416 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 6417 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 6418 if (may_throw) { 6419 zend_jit_check_exception(Dst); 6420 } 6421 6422 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 6423 | b >9 // END 6424 |.code 6425 |9: 6426 } 6427 } else if ((op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) 6428 && (!not_found_exit_addr || (var_info & MAY_BE_REF))) { 6429 |.cold_code 6430 |9: 6431 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, op1_data_info, 0, opline, ZREG_TMP1, ZREG_TMP2 6432 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 6433 if (may_throw) { 6434 zend_jit_check_exception(Dst); 6435 } 6436 | b >9 6437 |.code 6438 |9: 6439 } 6440 6441 return 1; 6442} 6443 6444static 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) 6445{ 6446 zend_jit_addr op1_addr, op2_addr; 6447 6448 ZEND_ASSERT(opline->op1_type == IS_CV && opline->result_type == IS_UNUSED); 6449 ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF)); 6450 6451 op1_addr = OP1_ADDR(); 6452 op2_addr = OP2_ADDR(); 6453 6454 if (op1_info & MAY_BE_REF) { 6455 binary_op_type binary_op = get_binary_op(opline->extended_value); 6456 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 6457 | IF_NOT_Z_TYPE, FCARG1x, IS_REFERENCE, >1, TMP1w 6458 | GET_Z_PTR FCARG1x, FCARG1x 6459 | ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)] 6460 | cbnz TMP1, >2 6461 | add FCARG1x, FCARG1x, #offsetof(zend_reference, val) 6462 |.cold_code 6463 |2: 6464 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 6465 | LOAD_ADDR CARG3, binary_op 6466 | SET_EX_OPLINE opline, REG0 6467 if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) 6468 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 6469 | EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0 6470 } else { 6471 | EXT_CALL zend_jit_assign_op_to_typed_ref, REG0 6472 } 6473 zend_jit_check_exception(Dst); 6474 | b >9 6475 |.code 6476 |1: 6477 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 6478 } 6479 6480 int result; 6481 switch (opline->extended_value) { 6482 case ZEND_ADD: 6483 case ZEND_SUB: 6484 case ZEND_MUL: 6485 case ZEND_DIV: 6486 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); 6487 break; 6488 case ZEND_BW_OR: 6489 case ZEND_BW_AND: 6490 case ZEND_BW_XOR: 6491 case ZEND_SL: 6492 case ZEND_SR: 6493 case ZEND_MOD: 6494 result = zend_jit_long_math_helper(Dst, opline, opline->extended_value, 6495 opline->op1_type, opline->op1, op1_addr, op1_info, op1_range, 6496 opline->op2_type, opline->op2, op2_addr, op2_info, op2_range, 6497 opline->op1.var, op1_addr, op1_def_info, op1_info, may_throw); 6498 break; 6499 case ZEND_CONCAT: 6500 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); 6501 break; 6502 default: 6503 ZEND_UNREACHABLE(); 6504 } 6505 |9: 6506 return result; 6507} 6508 6509static int zend_jit_cmp_long_long(dasm_State **Dst, 6510 const zend_op *opline, 6511 zend_ssa_range *op1_range, 6512 zend_jit_addr op1_addr, 6513 zend_ssa_range *op2_range, 6514 zend_jit_addr op2_addr, 6515 zend_jit_addr res_addr, 6516 zend_uchar smart_branch_opcode, 6517 uint32_t target_label, 6518 uint32_t target_label2, 6519 const void *exit_addr, 6520 bool skip_comparison) 6521{ 6522 bool swap = 0; 6523 bool result; 6524 6525 if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) { 6526 if (!smart_branch_opcode || 6527 smart_branch_opcode == ZEND_JMPZ_EX || 6528 smart_branch_opcode == ZEND_JMPNZ_EX) { 6529 | SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE), TMP1w, TMP2 6530 } 6531 if (smart_branch_opcode && !exit_addr) { 6532 if (smart_branch_opcode == ZEND_JMPZ || 6533 smart_branch_opcode == ZEND_JMPZ_EX) { 6534 if (!result) { 6535 | b => target_label 6536 } 6537 } else if (smart_branch_opcode == ZEND_JMPNZ || 6538 smart_branch_opcode == ZEND_JMPNZ_EX) { 6539 if (result) { 6540 | b => target_label 6541 } 6542 } else { 6543 ZEND_UNREACHABLE(); 6544 } 6545 } 6546 return 1; 6547 } 6548 6549 if (skip_comparison) { 6550 if (Z_MODE(op1_addr) != IS_REG && 6551 (Z_MODE(op2_addr) == IS_REG || 6552 (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL))) { 6553 swap = 1; 6554 } 6555 } else if (Z_MODE(op1_addr) == IS_REG) { 6556 if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { 6557 | cmp Rx(Z_REG(op1_addr)), xzr 6558 } else { 6559 | LONG_CMP Z_REG(op1_addr), op2_addr, TMP1 6560 } 6561 } else if (Z_MODE(op2_addr) == IS_REG) { 6562 if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 0) { 6563 | cmp Rx(Z_REG(op2_addr)), xzr 6564 } else { 6565 | LONG_CMP Z_REG(op2_addr), op1_addr, TMP1 6566 } 6567 swap = 1; 6568 } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL) { 6569 | LONG_CMP_WITH_CONST op2_addr, Z_LVAL_P(Z_ZV(op1_addr)), TMP1, TMP2 6570 swap = 1; 6571 } else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_MODE(op1_addr) != IS_CONST_ZVAL) { 6572 | LONG_CMP_WITH_CONST op1_addr, Z_LVAL_P(Z_ZV(op2_addr)), TMP1, TMP2 6573 } else { 6574 | GET_ZVAL_LVAL ZREG_REG0, op1_addr, TMP1 6575 if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { 6576 | cmp Rx(ZREG_REG0), xzr 6577 } else { 6578 | LONG_CMP ZREG_REG0, op2_addr, TMP1 6579 } 6580 } 6581 6582 if (smart_branch_opcode) { 6583 if (smart_branch_opcode == ZEND_JMPZ_EX || 6584 smart_branch_opcode == ZEND_JMPNZ_EX) { 6585 6586 switch (opline->opcode) { 6587 case ZEND_IS_EQUAL: 6588 case ZEND_IS_IDENTICAL: 6589 case ZEND_CASE: 6590 case ZEND_CASE_STRICT: 6591 | cset REG0w, eq 6592 break; 6593 case ZEND_IS_NOT_EQUAL: 6594 case ZEND_IS_NOT_IDENTICAL: 6595 | cset REG0w, ne 6596 break; 6597 case ZEND_IS_SMALLER: 6598 if (swap) { 6599 | cset REG0w, gt 6600 } else { 6601 | cset REG0w, lt 6602 } 6603 break; 6604 case ZEND_IS_SMALLER_OR_EQUAL: 6605 if (swap) { 6606 | cset REG0w, ge 6607 } else { 6608 | cset REG0w, le 6609 } 6610 break; 6611 default: 6612 ZEND_UNREACHABLE(); 6613 } 6614 | add REG0w, REG0w, #2 6615 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 6616 } 6617 if (smart_branch_opcode == ZEND_JMPZ || 6618 smart_branch_opcode == ZEND_JMPZ_EX) { 6619 switch (opline->opcode) { 6620 case ZEND_IS_EQUAL: 6621 case ZEND_IS_IDENTICAL: 6622 case ZEND_CASE: 6623 case ZEND_CASE_STRICT: 6624 if (exit_addr) { 6625 | bne &exit_addr 6626 } else { 6627 | bne => target_label 6628 } 6629 break; 6630 case ZEND_IS_NOT_EQUAL: 6631 if (exit_addr) { 6632 | beq &exit_addr 6633 } else { 6634 | beq => target_label 6635 } 6636 break; 6637 case ZEND_IS_NOT_IDENTICAL: 6638 if (exit_addr) { 6639 | bne &exit_addr 6640 } else { 6641 | beq => target_label 6642 } 6643 break; 6644 case ZEND_IS_SMALLER: 6645 if (swap) { 6646 if (exit_addr) { 6647 | ble &exit_addr 6648 } else { 6649 | ble => target_label 6650 } 6651 } else { 6652 if (exit_addr) { 6653 | bge &exit_addr 6654 } else { 6655 | bge => target_label 6656 } 6657 } 6658 break; 6659 case ZEND_IS_SMALLER_OR_EQUAL: 6660 if (swap) { 6661 if (exit_addr) { 6662 | blt &exit_addr 6663 } else { 6664 | blt => target_label 6665 } 6666 } else { 6667 if (exit_addr) { 6668 | bgt &exit_addr 6669 } else { 6670 | bgt => target_label 6671 } 6672 } 6673 break; 6674 default: 6675 ZEND_UNREACHABLE(); 6676 } 6677 } else if (smart_branch_opcode == ZEND_JMPNZ || 6678 smart_branch_opcode == ZEND_JMPNZ_EX) { 6679 switch (opline->opcode) { 6680 case ZEND_IS_EQUAL: 6681 case ZEND_IS_IDENTICAL: 6682 case ZEND_CASE: 6683 case ZEND_CASE_STRICT: 6684 if (exit_addr) { 6685 | beq &exit_addr 6686 } else { 6687 | beq => target_label 6688 } 6689 break; 6690 case ZEND_IS_NOT_EQUAL: 6691 if (exit_addr) { 6692 | bne &exit_addr 6693 } else { 6694 | bne => target_label 6695 } 6696 break; 6697 case ZEND_IS_NOT_IDENTICAL: 6698 if (exit_addr) { 6699 | beq &exit_addr 6700 } else { 6701 | bne => target_label 6702 } 6703 break; 6704 case ZEND_IS_SMALLER: 6705 if (swap) { 6706 if (exit_addr) { 6707 | bgt &exit_addr 6708 } else { 6709 | bgt => target_label 6710 } 6711 } else { 6712 if (exit_addr) { 6713 | blt &exit_addr 6714 } else { 6715 | blt => target_label 6716 } 6717 } 6718 break; 6719 case ZEND_IS_SMALLER_OR_EQUAL: 6720 if (swap) { 6721 if (exit_addr) { 6722 | bge &exit_addr 6723 } else { 6724 | bge => target_label 6725 } 6726 } else { 6727 if (exit_addr) { 6728 | ble &exit_addr 6729 } else { 6730 | ble => target_label 6731 } 6732 } 6733 break; 6734 default: 6735 ZEND_UNREACHABLE(); 6736 } 6737 } else { 6738 ZEND_UNREACHABLE(); 6739 } 6740 } else { 6741 switch (opline->opcode) { 6742 case ZEND_IS_EQUAL: 6743 case ZEND_IS_IDENTICAL: 6744 case ZEND_CASE: 6745 case ZEND_CASE_STRICT: 6746 | cset REG0w, eq 6747 break; 6748 case ZEND_IS_NOT_EQUAL: 6749 case ZEND_IS_NOT_IDENTICAL: 6750 | cset REG0w, ne 6751 break; 6752 case ZEND_IS_SMALLER: 6753 if (swap) { 6754 | cset REG0w, gt 6755 } else { 6756 | cset REG0w, lt 6757 } 6758 break; 6759 case ZEND_IS_SMALLER_OR_EQUAL: 6760 if (swap) { 6761 | cset REG0w, ge 6762 } else { 6763 | cset REG0w, le 6764 } 6765 break; 6766 default: 6767 ZEND_UNREACHABLE(); 6768 } 6769 | add REG0w, REG0w, #2 6770 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 6771 } 6772 6773 return 1; 6774} 6775 6776static 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) 6777{ 6778 if (smart_branch_opcode) { 6779 if (smart_branch_opcode == ZEND_JMPZ) { 6780 switch (opline->opcode) { 6781 case ZEND_IS_EQUAL: 6782 case ZEND_IS_IDENTICAL: 6783 case ZEND_CASE: 6784 case ZEND_CASE_STRICT: 6785 if (exit_addr) { 6786 | bne &exit_addr 6787 } else { 6788 | bne => target_label 6789 } 6790 break; 6791 case ZEND_IS_NOT_EQUAL: 6792 | bvs >1 6793 if (exit_addr) { 6794 | beq &exit_addr 6795 } else { 6796 | beq => target_label 6797 } 6798 |1: 6799 break; 6800 case ZEND_IS_NOT_IDENTICAL: 6801 if (exit_addr) { 6802 | bvs &exit_addr 6803 | bne &exit_addr 6804 } else { 6805 | bvs >1 6806 | beq => target_label 6807 |1: 6808 } 6809 break; 6810 case ZEND_IS_SMALLER: 6811 if (swap) { 6812 if (exit_addr) { 6813 | bvs &exit_addr 6814 | bls &exit_addr 6815 } else { 6816 | bvs => target_label 6817 | bls => target_label 6818 } 6819 } else { 6820 if (exit_addr) { 6821 | bhs &exit_addr 6822 } else { 6823 | bhs => target_label 6824 } 6825 } 6826 break; 6827 case ZEND_IS_SMALLER_OR_EQUAL: 6828 if (swap) { 6829 if (exit_addr) { 6830 | bvs &exit_addr 6831 | blo &exit_addr 6832 } else { 6833 | bvs => target_label 6834 | blo => target_label 6835 } 6836 } else { 6837 if (exit_addr) { 6838 | bhi &exit_addr 6839 } else { 6840 | bhi => target_label 6841 } 6842 } 6843 break; 6844 default: 6845 ZEND_UNREACHABLE(); 6846 } 6847 } else if (smart_branch_opcode == ZEND_JMPNZ) { 6848 switch (opline->opcode) { 6849 case ZEND_IS_EQUAL: 6850 case ZEND_IS_IDENTICAL: 6851 case ZEND_CASE: 6852 case ZEND_CASE_STRICT: 6853 | bvs >1 6854 if (exit_addr) { 6855 | beq &exit_addr 6856 } else { 6857 | beq => target_label 6858 } 6859 |1: 6860 break; 6861 case ZEND_IS_NOT_EQUAL: 6862 if (exit_addr) { 6863 | bne &exit_addr 6864 } else { 6865 | bne => target_label 6866 } 6867 break; 6868 case ZEND_IS_NOT_IDENTICAL: 6869 if (exit_addr) { 6870 | bvs >1 6871 | beq &exit_addr 6872 |1: 6873 } else { 6874 | bne => target_label 6875 } 6876 break; 6877 case ZEND_IS_SMALLER: 6878 if (swap) { 6879 | bvs >1 // Always False if involving NaN 6880 if (exit_addr) { 6881 | bhi &exit_addr 6882 } else { 6883 | bhi => target_label 6884 } 6885 |1: 6886 } else { 6887 | bvs >1 6888 if (exit_addr) { 6889 | blo &exit_addr 6890 } else { 6891 | blo => target_label 6892 } 6893 |1: 6894 } 6895 break; 6896 case ZEND_IS_SMALLER_OR_EQUAL: 6897 if (swap) { 6898 | bvs >1 // Always False if involving NaN 6899 if (exit_addr) { 6900 | bhs &exit_addr 6901 } else { 6902 | bhs => target_label 6903 } 6904 |1: 6905 } else { 6906 | bvs >1 6907 if (exit_addr) { 6908 | bls &exit_addr 6909 } else { 6910 | bls => target_label 6911 } 6912 |1: 6913 } 6914 break; 6915 default: 6916 ZEND_UNREACHABLE(); 6917 } 6918 } else if (smart_branch_opcode == ZEND_JMPZ_EX) { 6919 switch (opline->opcode) { 6920 case ZEND_IS_EQUAL: 6921 case ZEND_IS_IDENTICAL: 6922 case ZEND_CASE: 6923 case ZEND_CASE_STRICT: 6924 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6925 | bne => target_label 6926 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6927 break; 6928 case ZEND_IS_NOT_EQUAL: 6929 case ZEND_IS_NOT_IDENTICAL: 6930 | bvs >1 6931 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6932 | beq => target_label 6933 |1: 6934 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6935 break; 6936 case ZEND_IS_SMALLER: 6937 if (swap) { 6938 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6939 | bvs => target_label 6940 | bls => target_label 6941 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6942 } else { 6943 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6944 | bhs => target_label 6945 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6946 } 6947 break; 6948 case ZEND_IS_SMALLER_OR_EQUAL: 6949 if (swap) { 6950 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6951 | bvs => target_label 6952 | blo => target_label 6953 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6954 } else { 6955 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6956 | bhi => target_label 6957 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6958 } 6959 break; 6960 default: 6961 ZEND_UNREACHABLE(); 6962 } 6963 } else if (smart_branch_opcode == ZEND_JMPNZ_EX) { 6964 switch (opline->opcode) { 6965 case ZEND_IS_EQUAL: 6966 case ZEND_IS_IDENTICAL: 6967 case ZEND_CASE: 6968 case ZEND_CASE_STRICT: 6969 | bvs >1 6970 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6971 | beq => target_label 6972 |1: 6973 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6974 break; 6975 case ZEND_IS_NOT_EQUAL: 6976 case ZEND_IS_NOT_IDENTICAL: 6977 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6978 | bvs => target_label 6979 | bne => target_label 6980 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6981 break; 6982 case ZEND_IS_SMALLER: 6983 if (swap) { 6984 | cset REG0w, hi 6985 | add REG0w, REG0w, #2 6986 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 6987 | bvs >1 // Always False if involving NaN 6988 | bhi => target_label 6989 |1: 6990 } else { 6991 | bvs >1 6992 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 6993 | blo => target_label 6994 |1: 6995 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 6996 } 6997 break; 6998 case ZEND_IS_SMALLER_OR_EQUAL: 6999 if (swap) { 7000 | cset REG0w, hs 7001 | add REG0w, REG0w, #2 7002 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 7003 | bvs >1 // Always False if involving NaN 7004 | bhs => target_label 7005 |1: 7006 } else { 7007 | bvs >1 7008 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 7009 | bls => target_label 7010 |1: 7011 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 7012 } 7013 break; 7014 default: 7015 ZEND_UNREACHABLE(); 7016 } 7017 } else { 7018 ZEND_UNREACHABLE(); 7019 } 7020 } else { 7021 switch (opline->opcode) { 7022 case ZEND_IS_EQUAL: 7023 case ZEND_IS_IDENTICAL: 7024 case ZEND_CASE: 7025 case ZEND_CASE_STRICT: 7026 | bvs >1 7027 | mov REG0, #IS_TRUE 7028 | beq >2 7029 |1: 7030 | mov REG0, #IS_FALSE 7031 |2: 7032 break; 7033 case ZEND_IS_NOT_EQUAL: 7034 case ZEND_IS_NOT_IDENTICAL: 7035 | bvs >1 7036 | mov REG0, #IS_FALSE 7037 | beq >2 7038 |1: 7039 | mov REG0, #IS_TRUE 7040 |2: 7041 break; 7042 case ZEND_IS_SMALLER: 7043 | bvs >1 7044 | mov REG0, #IS_TRUE 7045 || if (swap) { 7046 | bhi >2 7047 || } else { 7048 | blo >2 7049 || } 7050 |1: 7051 | mov REG0, #IS_FALSE 7052 |2: 7053 break; 7054 case ZEND_IS_SMALLER_OR_EQUAL: 7055 | bvs >1 7056 | mov REG0, #IS_TRUE 7057 || if (swap) { 7058 | bhs >2 7059 || } else { 7060 | bls >2 7061 || } 7062 |1: 7063 | mov REG0, #IS_FALSE 7064 |2: 7065 break; 7066 default: 7067 ZEND_UNREACHABLE(); 7068 } 7069 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 7070 } 7071 7072 return 1; 7073} 7074 7075static 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) 7076{ 7077 zend_reg tmp_reg = ZREG_FPR0; 7078 7079 | DOUBLE_GET_ZVAL_LVAL tmp_reg, op1_addr, ZREG_REG0, ZREG_TMP1 7080 | DOUBLE_CMP tmp_reg, op2_addr, ZREG_TMP1, ZREG_FPTMP 7081 7082 return zend_jit_cmp_double_common(Dst, opline, res_addr, 0, smart_branch_opcode, target_label, target_label2, exit_addr); 7083} 7084 7085static 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) 7086{ 7087 zend_reg tmp_reg = ZREG_FPR0; 7088 7089 | DOUBLE_GET_ZVAL_LVAL tmp_reg, op2_addr, ZREG_REG0, ZREG_TMP1 7090 | DOUBLE_CMP tmp_reg, op1_addr, ZREG_TMP1, ZREG_FPTMP 7091 7092 return zend_jit_cmp_double_common(Dst, opline, res_addr, /* swap */ 1, smart_branch_opcode, target_label, target_label2, exit_addr); 7093} 7094 7095static 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) 7096{ 7097 bool swap = 0; 7098 7099 if (Z_MODE(op1_addr) == IS_REG) { 7100 | DOUBLE_CMP Z_REG(op1_addr), op2_addr, ZREG_TMP1, ZREG_FPTMP 7101 } else if (Z_MODE(op2_addr) == IS_REG) { 7102 | DOUBLE_CMP Z_REG(op2_addr), op1_addr, ZREG_TMP1, ZREG_FPTMP 7103 swap = 1; 7104 } else { 7105 zend_reg tmp_reg = ZREG_FPR0; 7106 7107 | GET_ZVAL_DVAL tmp_reg, op1_addr, ZREG_TMP1 7108 | DOUBLE_CMP tmp_reg, op2_addr, ZREG_TMP1, ZREG_FPTMP 7109 } 7110 7111 return zend_jit_cmp_double_common(Dst, opline, res_addr, swap, smart_branch_opcode, target_label, target_label2, exit_addr); 7112} 7113 7114static 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) 7115{ 7116 | tst RETVALw, RETVALw 7117 if (smart_branch_opcode) { 7118 if (smart_branch_opcode == ZEND_JMPZ_EX || 7119 smart_branch_opcode == ZEND_JMPNZ_EX) { 7120 switch (opline->opcode) { 7121 case ZEND_IS_EQUAL: 7122 case ZEND_CASE: 7123 | cset REG0w, eq 7124 break; 7125 case ZEND_IS_NOT_EQUAL: 7126 | cset REG0w, ne 7127 break; 7128 case ZEND_IS_SMALLER: 7129 | cset REG0w, lt 7130 break; 7131 case ZEND_IS_SMALLER_OR_EQUAL: 7132 | cset REG0w, le 7133 break; 7134 default: 7135 ZEND_UNREACHABLE(); 7136 } 7137 | add REG0w, REG0w, #2 7138 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 7139 } 7140 if (smart_branch_opcode == ZEND_JMPZ || 7141 smart_branch_opcode == ZEND_JMPZ_EX) { 7142 switch (opline->opcode) { 7143 case ZEND_IS_EQUAL: 7144 case ZEND_CASE: 7145 if (exit_addr) { 7146 | bne &exit_addr 7147 } else { 7148 | bne => target_label 7149 } 7150 break; 7151 case ZEND_IS_NOT_EQUAL: 7152 if (exit_addr) { 7153 | beq &exit_addr 7154 } else { 7155 | beq => target_label 7156 } 7157 break; 7158 case ZEND_IS_SMALLER: 7159 if (exit_addr) { 7160 | bge &exit_addr 7161 } else { 7162 | bge => target_label 7163 } 7164 break; 7165 case ZEND_IS_SMALLER_OR_EQUAL: 7166 if (exit_addr) { 7167 | bgt &exit_addr 7168 } else { 7169 | bgt => target_label 7170 } 7171 break; 7172 default: 7173 ZEND_UNREACHABLE(); 7174 } 7175 } else if (smart_branch_opcode == ZEND_JMPNZ || 7176 smart_branch_opcode == ZEND_JMPNZ_EX) { 7177 switch (opline->opcode) { 7178 case ZEND_IS_EQUAL: 7179 case ZEND_CASE: 7180 if (exit_addr) { 7181 | beq &exit_addr 7182 } else { 7183 | beq => target_label 7184 } 7185 break; 7186 case ZEND_IS_NOT_EQUAL: 7187 if (exit_addr) { 7188 | bne &exit_addr 7189 } else { 7190 | bne => target_label 7191 } 7192 break; 7193 case ZEND_IS_SMALLER: 7194 if (exit_addr) { 7195 | blt &exit_addr 7196 } else { 7197 | blt => target_label 7198 } 7199 break; 7200 case ZEND_IS_SMALLER_OR_EQUAL: 7201 if (exit_addr) { 7202 | ble &exit_addr 7203 } else { 7204 | ble => target_label 7205 } 7206 break; 7207 default: 7208 ZEND_UNREACHABLE(); 7209 } 7210 } else { 7211 ZEND_UNREACHABLE(); 7212 } 7213 } else { 7214 switch (opline->opcode) { 7215 case ZEND_IS_EQUAL: 7216 case ZEND_CASE: 7217 | cset REG0w, eq 7218 break; 7219 case ZEND_IS_NOT_EQUAL: 7220 | cset REG0w, ne 7221 break; 7222 case ZEND_IS_SMALLER: 7223 | cset REG0w, lt 7224 break; 7225 case ZEND_IS_SMALLER_OR_EQUAL: 7226 | cset REG0w, le 7227 break; 7228 default: 7229 ZEND_UNREACHABLE(); 7230 } 7231 | add REG0w, REG0w, #2 7232 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 7233 } 7234 7235 return 1; 7236} 7237 7238static int zend_jit_cmp(dasm_State **Dst, 7239 const zend_op *opline, 7240 uint32_t op1_info, 7241 zend_ssa_range *op1_range, 7242 zend_jit_addr op1_addr, 7243 uint32_t op2_info, 7244 zend_ssa_range *op2_range, 7245 zend_jit_addr op2_addr, 7246 zend_jit_addr res_addr, 7247 int may_throw, 7248 zend_uchar smart_branch_opcode, 7249 uint32_t target_label, 7250 uint32_t target_label2, 7251 const void *exit_addr, 7252 bool skip_comparison) 7253{ 7254 bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var); 7255 bool has_slow; 7256 7257 has_slow = 7258 (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 7259 (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) && 7260 ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) || 7261 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))); 7262 7263 if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) { 7264 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) { 7265 if (op1_info & MAY_BE_DOUBLE) { 7266 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >4, ZREG_TMP1 7267 } else { 7268 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9, ZREG_TMP1 7269 } 7270 } 7271 if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))) { 7272 if (op2_info & MAY_BE_DOUBLE) { 7273 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3, ZREG_TMP1 7274 |.cold_code 7275 |3: 7276 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) { 7277 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1 7278 } 7279 if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7280 return 0; 7281 } 7282 | b >6 7283 |.code 7284 } else { 7285 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1 7286 } 7287 } 7288 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)) { 7289 return 0; 7290 } 7291 if (op1_info & MAY_BE_DOUBLE) { 7292 |.cold_code 7293 |4: 7294 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) { 7295 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1 7296 } 7297 if (op2_info & MAY_BE_DOUBLE) { 7298 if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) { 7299 if (!same_ops) { 7300 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >5, ZREG_TMP1 7301 } else { 7302 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1 7303 } 7304 } 7305 if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7306 return 0; 7307 } 7308 | b >6 7309 } 7310 if (!same_ops) { 7311 |5: 7312 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) { 7313 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1 7314 } 7315 if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7316 return 0; 7317 } 7318 | b >6 7319 } 7320 |.code 7321 } 7322 } else if ((op1_info & MAY_BE_DOUBLE) && 7323 !(op1_info & MAY_BE_LONG) && 7324 (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) { 7325 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) { 7326 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1 7327 } 7328 if (op2_info & MAY_BE_DOUBLE) { 7329 if (!same_ops && (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) { 7330 if (!same_ops && (op2_info & MAY_BE_LONG)) { 7331 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >3, ZREG_TMP1 7332 } else { 7333 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1 7334 } 7335 } 7336 if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7337 return 0; 7338 } 7339 } 7340 if (!same_ops && (op2_info & MAY_BE_LONG)) { 7341 if (op2_info & MAY_BE_DOUBLE) { 7342 |.cold_code 7343 } 7344 |3: 7345 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) { 7346 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9, ZREG_TMP1 7347 } 7348 if (!zend_jit_cmp_double_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7349 return 0; 7350 } 7351 if (op2_info & MAY_BE_DOUBLE) { 7352 | b >6 7353 |.code 7354 } 7355 } 7356 } else if ((op2_info & MAY_BE_DOUBLE) && 7357 !(op2_info & MAY_BE_LONG) && 7358 (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) { 7359 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) { 7360 | IF_NOT_ZVAL_TYPE op2_addr, IS_DOUBLE, >9, ZREG_TMP1 7361 } 7362 if (op1_info & MAY_BE_DOUBLE) { 7363 if (!same_ops && (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) { 7364 if (!same_ops && (op1_info & MAY_BE_LONG)) { 7365 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >3, ZREG_TMP1 7366 } else { 7367 | IF_NOT_ZVAL_TYPE op1_addr, IS_DOUBLE, >9, ZREG_TMP1 7368 } 7369 } 7370 if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7371 return 0; 7372 } 7373 } 7374 if (!same_ops && (op1_info & MAY_BE_LONG)) { 7375 if (op1_info & MAY_BE_DOUBLE) { 7376 |.cold_code 7377 } 7378 |3: 7379 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_DOUBLE|MAY_BE_LONG))) { 7380 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >9, ZREG_TMP1 7381 } 7382 if (!zend_jit_cmp_long_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7383 return 0; 7384 } 7385 if (op1_info & MAY_BE_DOUBLE) { 7386 | b >6 7387 |.code 7388 } 7389 } 7390 } 7391 7392 if (has_slow || 7393 (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) || 7394 (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) { 7395 if (has_slow) { 7396 |.cold_code 7397 |9: 7398 } 7399 | SET_EX_OPLINE opline, REG0 7400 if (Z_MODE(op1_addr) == IS_REG) { 7401 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); 7402 if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) { 7403 return 0; 7404 } 7405 op1_addr = real_addr; 7406 } 7407 if (Z_MODE(op2_addr) == IS_REG) { 7408 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var); 7409 if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) { 7410 return 0; 7411 } 7412 op2_addr = real_addr; 7413 } 7414 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 7415 if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) { 7416 | IF_NOT_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w 7417 | LOAD_32BIT_VAL FCARG1x, opline->op1.var 7418 | EXT_CALL zend_jit_undefined_op_helper, REG0 7419 | LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval 7420 |1: 7421 } 7422 if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) { 7423 | IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1 7424 | str FCARG1x, T1 // save 7425 | LOAD_32BIT_VAL FCARG1x, opline->op2.var 7426 | EXT_CALL zend_jit_undefined_op_helper, REG0 7427 | ldr FCARG1x, T1 // restore 7428 | LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval 7429 | b >2 7430 |1: 7431 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 7432 |2: 7433 } else { 7434 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 7435 } 7436 | EXT_CALL zend_compare, REG0 7437 if ((opline->opcode != ZEND_CASE && 7438 (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 7439 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) || 7440 ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && 7441 (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) { 7442 | str RETVALw, T1 // save 7443 if (opline->opcode != ZEND_CASE) { 7444 | FREE_OP opline->op1_type, opline->op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 7445 } 7446 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 7447 | ldr RETVALw, T1 // restore 7448 } 7449 if (may_throw) { 7450 zend_jit_check_exception_undef_result(Dst, opline); 7451 } 7452 if (!zend_jit_cmp_slow(Dst, opline, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7453 return 0; 7454 } 7455 if (has_slow) { 7456 | b >6 7457 |.code 7458 } 7459 } 7460 7461 |6: 7462 7463 return 1; 7464} 7465 7466static int zend_jit_identical(dasm_State **Dst, 7467 const zend_op *opline, 7468 uint32_t op1_info, 7469 zend_ssa_range *op1_range, 7470 zend_jit_addr op1_addr, 7471 uint32_t op2_info, 7472 zend_ssa_range *op2_range, 7473 zend_jit_addr op2_addr, 7474 zend_jit_addr res_addr, 7475 int may_throw, 7476 zend_uchar smart_branch_opcode, 7477 uint32_t target_label, 7478 uint32_t target_label2, 7479 const void *exit_addr, 7480 bool skip_comparison) 7481{ 7482 uint32_t identical_label = (uint32_t)-1; 7483 uint32_t not_identical_label = (uint32_t)-1; 7484 7485 if (smart_branch_opcode && !exit_addr) { 7486 if (opline->opcode != ZEND_IS_NOT_IDENTICAL) { 7487 if (smart_branch_opcode == ZEND_JMPZ) { 7488 not_identical_label = target_label; 7489 } else if (smart_branch_opcode == ZEND_JMPNZ) { 7490 identical_label = target_label; 7491 } else { 7492 ZEND_UNREACHABLE(); 7493 } 7494 } else { 7495 if (smart_branch_opcode == ZEND_JMPZ) { 7496 identical_label = target_label; 7497 } else if (smart_branch_opcode == ZEND_JMPNZ) { 7498 not_identical_label = target_label; 7499 } else { 7500 ZEND_UNREACHABLE(); 7501 } 7502 } 7503 } 7504 7505 if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && 7506 (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) { 7507 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)) { 7508 return 0; 7509 } 7510 return 1; 7511 } else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE && 7512 (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) { 7513 if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { 7514 return 0; 7515 } 7516 return 1; 7517 } 7518 7519 if ((op1_info & MAY_BE_UNDEF) && (op2_info & MAY_BE_UNDEF)) { 7520 op1_info |= MAY_BE_NULL; 7521 op2_info |= MAY_BE_NULL; 7522 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 7523 | IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w 7524 |.cold_code 7525 |1: 7526 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 7527 | SET_EX_OPLINE opline, REG0 7528 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 7529 | EXT_CALL zend_jit_undefined_op_helper, REG0 7530 if (may_throw) { 7531 zend_jit_check_exception_undef_result(Dst, opline); 7532 } 7533 | LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval 7534 | b >1 7535 |.code 7536 |1: 7537 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 7538 | IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w 7539 |.cold_code 7540 |1: 7541 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 7542 | SET_EX_OPLINE opline, REG0 7543 | str FCARG1x, T1 // save 7544 | LOAD_32BIT_VAL FCARG1w, opline->op2.var 7545 | EXT_CALL zend_jit_undefined_op_helper, REG0 7546 if (may_throw) { 7547 zend_jit_check_exception_undef_result(Dst, opline); 7548 } 7549 | ldr FCARG1x, T1 // restore 7550 | LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval 7551 | b >1 7552 |.code 7553 |1: 7554 } else if (op1_info & MAY_BE_UNDEF) { 7555 op1_info |= MAY_BE_NULL; 7556 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 7557 | IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w 7558 |.cold_code 7559 |1: 7560 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 7561 | SET_EX_OPLINE opline, REG0 7562 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 7563 | EXT_CALL zend_jit_undefined_op_helper, REG0 7564 if (may_throw) { 7565 zend_jit_check_exception_undef_result(Dst, opline); 7566 } 7567 | LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval 7568 | b >1 7569 |.code 7570 |1: 7571 if (opline->op2_type != IS_CONST) { 7572 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 7573 } 7574 } else if (op2_info & MAY_BE_UNDEF) { 7575 op2_info |= MAY_BE_NULL; 7576 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 7577 | IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w 7578 |.cold_code 7579 |1: 7580 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 7581 | SET_EX_OPLINE opline, REG0 7582 | LOAD_32BIT_VAL FCARG1w, opline->op2.var 7583 | EXT_CALL zend_jit_undefined_op_helper, REG0 7584 if (may_throw) { 7585 zend_jit_check_exception_undef_result(Dst, opline); 7586 } 7587 | LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval 7588 | b >1 7589 |.code 7590 |1: 7591 if (opline->op1_type != IS_CONST) { 7592 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 7593 } 7594 } else if ((op1_info & op2_info & MAY_BE_ANY) != 0) { 7595 if (opline->op1_type != IS_CONST) { 7596 if (Z_MODE(op1_addr) == IS_REG) { 7597 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); 7598 if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) { 7599 return 0; 7600 } 7601 op1_addr = real_addr; 7602 } 7603 } 7604 if (opline->op2_type != IS_CONST) { 7605 if (Z_MODE(op2_addr) == IS_REG) { 7606 zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var); 7607 if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) { 7608 return 0; 7609 } 7610 op2_addr = real_addr; 7611 } 7612 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 7613 } 7614 if (opline->op1_type != IS_CONST) { 7615 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 7616 } 7617 } 7618 7619 if ((op1_info & op2_info & MAY_BE_ANY) == 0) { 7620 if ((opline->opcode != ZEND_CASE_STRICT && 7621 (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 7622 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) || 7623 ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && 7624 (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) { 7625 if (opline->opcode != ZEND_CASE_STRICT) { 7626 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7627 } 7628 | FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7629 } 7630 if (smart_branch_opcode) { 7631 if (may_throw) { 7632 zend_jit_check_exception_undef_result(Dst, opline); 7633 } 7634 if (exit_addr) { 7635 if (smart_branch_opcode == ZEND_JMPZ) { 7636 | b &exit_addr 7637 } 7638 } else if (not_identical_label != (uint32_t)-1) { 7639 | b =>not_identical_label 7640 } 7641 } else { 7642 | SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2 7643 if (may_throw) { 7644 zend_jit_check_exception(Dst); 7645 } 7646 } 7647 return 1; 7648 } 7649 7650 if (opline->op1_type & (IS_CV|IS_VAR)) { 7651 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 7652 } 7653 if (opline->op2_type & (IS_CV|IS_VAR)) { 7654 | ZVAL_DEREF FCARG2x, op2_info, TMP1w 7655 } 7656 7657 if (has_concrete_type(op1_info) 7658 && has_concrete_type(op2_info) 7659 && concrete_type(op1_info) == concrete_type(op2_info) 7660 && concrete_type(op1_info) <= IS_TRUE) { 7661 if (smart_branch_opcode) { 7662 if (exit_addr) { 7663 if (smart_branch_opcode == ZEND_JMPNZ) { 7664 | b &exit_addr 7665 } 7666 } else if (identical_label != (uint32_t)-1) { 7667 | b =>identical_label 7668 } 7669 } else { 7670 | SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2 7671 } 7672 } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) == IS_CONST_ZVAL) { 7673 if (zend_is_identical(Z_ZV(op1_addr), Z_ZV(op2_addr))) { 7674 if (smart_branch_opcode) { 7675 if (exit_addr) { 7676 if (smart_branch_opcode == ZEND_JMPNZ) { 7677 | b &exit_addr 7678 } 7679 } else if (identical_label != (uint32_t)-1) { 7680 | b =>identical_label 7681 } 7682 } else { 7683 | SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2 7684 } 7685 } else { 7686 if (smart_branch_opcode) { 7687 if (exit_addr) { 7688 if (smart_branch_opcode == ZEND_JMPZ) { 7689 | b &exit_addr 7690 } 7691 } else if (not_identical_label != (uint32_t)-1) { 7692 | b =>not_identical_label 7693 } 7694 } else { 7695 | SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2 7696 } 7697 } 7698 } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op1_addr)) <= IS_TRUE) { 7699 zval *val = Z_ZV(op1_addr); 7700 7701 | ldrb TMP1w, [FCARG2x, #offsetof(zval, u1.v.type)] 7702 | cmp TMP1w, #Z_TYPE_P(val) 7703 if (smart_branch_opcode) { 7704 if (opline->op2_type == IS_VAR && (op2_info & MAY_BE_REF)) { 7705 | bne >8 7706 | FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7707 if (may_throw) { 7708 zend_jit_check_exception_undef_result(Dst, opline); 7709 } 7710 if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) { 7711 | b &exit_addr 7712 } else if (identical_label != (uint32_t)-1) { 7713 | b =>identical_label 7714 } else { 7715 | b >9 7716 } 7717 |8: 7718 } else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) { 7719 | beq &exit_addr 7720 } else if (identical_label != (uint32_t)-1) { 7721 | beq =>identical_label 7722 } else { 7723 | beq >9 7724 } 7725 } else { 7726 if (opline->opcode != ZEND_IS_NOT_IDENTICAL) { 7727 | cset REG0w, eq 7728 } else { 7729 | cset REG0w, ne 7730 } 7731 | add REG0w, REG0w, #2 7732 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 7733 } 7734 if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && 7735 (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { 7736 | FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7737 if (may_throw) { 7738 zend_jit_check_exception_undef_result(Dst, opline); 7739 } 7740 } 7741 if (exit_addr) { 7742 if (smart_branch_opcode == ZEND_JMPZ) { 7743 | b &exit_addr 7744 } 7745 } else if (smart_branch_opcode && not_identical_label != (uint32_t)-1) { 7746 | b =>not_identical_label 7747 } 7748 } else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op2_addr)) <= IS_TRUE) { 7749 zval *val = Z_ZV(op2_addr); 7750 7751 | ldrb TMP1w, [FCARG1x, #offsetof(zval, u1.v.type)] 7752 | cmp TMP1w, #Z_TYPE_P(val) 7753 if (smart_branch_opcode) { 7754 if (opline->opcode != ZEND_CASE_STRICT 7755 && opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) { 7756 | bne >8 7757 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7758 if (may_throw) { 7759 zend_jit_check_exception_undef_result(Dst, opline); 7760 } 7761 if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) { 7762 | b &exit_addr 7763 } else if (identical_label != (uint32_t)-1) { 7764 | b =>identical_label 7765 } else { 7766 | b >9 7767 } 7768 |8: 7769 } else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) { 7770 | beq &exit_addr 7771 } else if (identical_label != (uint32_t)-1) { 7772 | beq =>identical_label 7773 } else { 7774 | beq >9 7775 } 7776 } else { 7777 if (opline->opcode != ZEND_IS_NOT_IDENTICAL) { 7778 | cset REG0w, eq 7779 } else { 7780 | cset REG0w, ne 7781 } 7782 | add REG0w, REG0w, #2 7783 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 7784 } 7785 if (opline->opcode != ZEND_CASE_STRICT 7786 && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 7787 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { 7788 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7789 if (may_throw) { 7790 zend_jit_check_exception_undef_result(Dst, opline); 7791 } 7792 } 7793 if (smart_branch_opcode) { 7794 if (exit_addr) { 7795 if (smart_branch_opcode == ZEND_JMPZ) { 7796 | b &exit_addr 7797 } 7798 } else if (not_identical_label != (uint32_t)-1) { 7799 | b =>not_identical_label 7800 } 7801 } 7802 } else { 7803 if (opline->op1_type == IS_CONST) { 7804 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 7805 } 7806 if (opline->op2_type == IS_CONST) { 7807 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 7808 } 7809 | EXT_CALL zend_is_identical, REG0 7810 if ((opline->opcode != ZEND_CASE_STRICT && 7811 (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 7812 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) || 7813 ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && 7814 (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) { 7815 | str RETVALw, T1 // save 7816 if (opline->opcode != ZEND_CASE_STRICT) { 7817 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7818 } 7819 | FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2 7820 if (may_throw) { 7821 zend_jit_check_exception_undef_result(Dst, opline); 7822 } 7823 | ldr RETVALw, T1 // restore 7824 } 7825 if (smart_branch_opcode) { 7826 if (exit_addr) { 7827 if (smart_branch_opcode == ZEND_JMPNZ) { 7828 | cbnz RETVALw, &exit_addr 7829 } else { 7830 | cbz RETVALw, &exit_addr 7831 } 7832 } else if (not_identical_label != (uint32_t)-1) { 7833 | cbz RETVALw, =>not_identical_label 7834 if (identical_label != (uint32_t)-1) { 7835 | b =>identical_label 7836 } 7837 } else if (identical_label != (uint32_t)-1) { 7838 | cbnz RETVALw, =>identical_label 7839 } 7840 } else { 7841 if (opline->opcode != ZEND_IS_NOT_IDENTICAL) { 7842 | add RETVALw, RETVALw, #2 7843 } else { 7844 | neg RETVALw, RETVALw 7845 | add RETVALw, RETVALw, #3 7846 } 7847 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, RETVALw, TMP1 7848 } 7849 } 7850 7851 |9: 7852 if (may_throw) { 7853 zend_jit_check_exception(Dst); 7854 } 7855 return 1; 7856} 7857 7858static 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) 7859{ 7860 uint32_t true_label = -1; 7861 uint32_t false_label = -1; 7862 bool set_bool = 0; 7863 bool set_bool_not = 0; 7864 bool set_delayed = 0; 7865 bool jmp_done = 0; 7866 7867 if (branch_opcode == ZEND_BOOL) { 7868 set_bool = 1; 7869 } else if (branch_opcode == ZEND_BOOL_NOT) { 7870 set_bool = 1; 7871 set_bool_not = 1; 7872 } else if (branch_opcode == ZEND_JMPZ) { 7873 false_label = target_label; 7874 } else if (branch_opcode == ZEND_JMPNZ) { 7875 true_label = target_label; 7876 } else if (branch_opcode == ZEND_JMPZ_EX) { 7877 set_bool = 1; 7878 false_label = target_label; 7879 } else if (branch_opcode == ZEND_JMPNZ_EX) { 7880 set_bool = 1; 7881 true_label = target_label; 7882 } else { 7883 ZEND_UNREACHABLE(); 7884 } 7885 7886 if (Z_MODE(op1_addr) == IS_CONST_ZVAL) { 7887 if (zend_is_true(Z_ZV(op1_addr))) { 7888 /* Always TRUE */ 7889 if (set_bool) { 7890 if (set_bool_not) { 7891 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 7892 } else { 7893 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 7894 } 7895 } 7896 if (true_label != (uint32_t)-1) { 7897 | b =>true_label 7898 } 7899 } else { 7900 /* Always FALSE */ 7901 if (set_bool) { 7902 if (set_bool_not) { 7903 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 7904 } else { 7905 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 7906 } 7907 } 7908 if (false_label != (uint32_t)-1) { 7909 | b =>false_label 7910 } 7911 } 7912 return 1; 7913 } 7914 7915 if (opline->op1_type == IS_CV && (op1_info & MAY_BE_REF)) { 7916 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 7917 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 7918 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 7919 } 7920 7921 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE)) { 7922 if (!(op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_TRUE))) { 7923 /* Always TRUE */ 7924 if (set_bool) { 7925 if (set_bool_not) { 7926 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 7927 } else { 7928 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 7929 } 7930 } 7931 if (true_label != (uint32_t)-1) { 7932 | b =>true_label 7933 } 7934 } else { 7935 if (!(op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE)))) { 7936 /* Always FALSE */ 7937 if (set_bool) { 7938 if (set_bool_not) { 7939 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 7940 } else { 7941 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 7942 } 7943 } 7944 } else { 7945 | CMP_ZVAL_TYPE op1_addr, IS_TRUE, ZREG_TMP1 7946 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE))) { 7947 if ((op1_info & MAY_BE_LONG) && 7948 !(op1_info & MAY_BE_UNDEF) && 7949 !set_bool) { 7950 if (exit_addr) { 7951 if (branch_opcode == ZEND_JMPNZ) { 7952 | blt >9 7953 } else { 7954 | blt &exit_addr 7955 } 7956 } else if (false_label != (uint32_t)-1) { 7957 | blt =>false_label 7958 } else { 7959 | blt >9 7960 } 7961 jmp_done = 1; 7962 } else { 7963 | bgt >2 7964 } 7965 } 7966 if (!(op1_info & MAY_BE_TRUE)) { 7967 /* It's FALSE */ 7968 if (set_bool) { 7969 if (set_bool_not) { 7970 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 7971 } else { 7972 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 7973 } 7974 } 7975 } else { 7976 if (exit_addr) { 7977 if (set_bool) { 7978 | bne >1 7979 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 7980 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 7981 | b &exit_addr 7982 } else { 7983 | b >9 7984 } 7985 |1: 7986 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 7987 if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) { 7988 if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) { 7989 | bne &exit_addr 7990 } 7991 } 7992 } else { 7993 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 7994 | beq &exit_addr 7995 } else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) { 7996 | bne &exit_addr 7997 } else { 7998 | beq >9 7999 } 8000 } 8001 } else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) { 8002 if (set_bool) { 8003 | bne >1 8004 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 8005 if (true_label != (uint32_t)-1) { 8006 | b =>true_label 8007 } else { 8008 | b >9 8009 } 8010 |1: 8011 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 8012 } else { 8013 if (true_label != (uint32_t)-1) { 8014 | beq =>true_label 8015 } else if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_LONG))) { 8016 | bne =>false_label 8017 jmp_done = 1; 8018 } else { 8019 | beq >9 8020 } 8021 } 8022 } else if (set_bool) { 8023 | cset REG0w, eq 8024 if (set_bool_not) { 8025 | neg REG0w, REG0w 8026 | add REG0w, REG0w, #3 8027 } else { 8028 | add REG0w, REG0w, #2 8029 } 8030 if ((op1_info & MAY_BE_UNDEF) && (op1_info & MAY_BE_ANY)) { 8031 set_delayed = 1; 8032 } else { 8033 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 8034 } 8035 } 8036 } 8037 } 8038 8039 /* It's FALSE, but may be UNDEF */ 8040 if (op1_info & MAY_BE_UNDEF) { 8041 if (op1_info & MAY_BE_ANY) { 8042 if (set_delayed) { 8043 | CMP_ZVAL_TYPE op1_addr, IS_UNDEF, ZREG_TMP1 8044 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 8045 | beq >1 8046 } else { 8047 | IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 8048 } 8049 |.cold_code 8050 |1: 8051 } 8052 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 8053 | SET_EX_OPLINE opline, REG0 8054 | EXT_CALL zend_jit_undefined_op_helper, REG0 8055 8056 if (may_throw) { 8057 if (!zend_jit_check_exception_undef_result(Dst, opline)) { 8058 return 0; 8059 } 8060 } 8061 8062 if (exit_addr) { 8063 if (branch_opcode == ZEND_JMPZ || branch_opcode == ZEND_JMPZ_EX) { 8064 | b &exit_addr 8065 } 8066 } else if (false_label != (uint32_t)-1) { 8067 | b =>false_label 8068 } 8069 if (op1_info & MAY_BE_ANY) { 8070 if (exit_addr) { 8071 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 8072 | b >9 8073 } 8074 } else if (false_label == (uint32_t)-1) { 8075 | b >9 8076 } 8077 |.code 8078 } 8079 } 8080 8081 if (!jmp_done) { 8082 if (exit_addr) { 8083 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 8084 if (op1_info & MAY_BE_LONG) { 8085 | b >9 8086 } 8087 } else if (op1_info & MAY_BE_LONG) { 8088 | b &exit_addr 8089 } 8090 } else if (false_label != (uint32_t)-1) { 8091 | b =>false_label 8092 } else if ((op1_info & MAY_BE_LONG) || (op1_info & MAY_BE_ANY) == MAY_BE_DOUBLE) { 8093 | b >9 8094 } 8095 } 8096 } 8097 } 8098 8099 if (op1_info & MAY_BE_LONG) { 8100 |2: 8101 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) { 8102 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >2, ZREG_TMP1 8103 } 8104 if (Z_MODE(op1_addr) == IS_REG) { 8105 | tst Rx(Z_REG(op1_addr)), Rx(Z_REG(op1_addr)) 8106 } else { 8107 | LONG_CMP_WITH_CONST op1_addr, Z_L(0), TMP1, TMP2 8108 } 8109 if (set_bool) { 8110 | cset REG0w, ne 8111 if (set_bool_not) { 8112 | neg REG0w, REG0w 8113 | add REG0w, REG0w, #3 8114 } else { 8115 | add REG0w, REG0w, #2 8116 } 8117 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 8118 } 8119 if (exit_addr) { 8120 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 8121 | bne &exit_addr 8122 } else { 8123 | beq &exit_addr 8124 } 8125 } else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) { 8126 if (true_label != (uint32_t)-1) { 8127 | bne =>true_label 8128 if (false_label != (uint32_t)-1) { 8129 | b =>false_label 8130 } 8131 } else { 8132 | beq =>false_label 8133 } 8134 } 8135 } 8136 8137 if ((op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) == MAY_BE_DOUBLE) { 8138 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8139 |.cold_code 8140 } 8141 |2: 8142 | fmov FPR0, xzr // TODO: "movi d0, #0" is not recognized by DynASM/arm64 8143 | DOUBLE_CMP ZREG_FPR0, op1_addr, ZREG_TMP1, ZREG_FPTMP 8144 8145 if (set_bool) { 8146 if (exit_addr) { 8147 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 8148 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 8149 | bvs &exit_addr 8150 | bne &exit_addr 8151 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 8152 } else { 8153 | bvs >1 8154 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 8155 | beq &exit_addr 8156 |1: 8157 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 8158 } 8159 } else if (false_label != (uint32_t)-1) { // JMPZ_EX 8160 | bvs >1 8161 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 8162 | beq => false_label 8163 |1: 8164 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 8165 } else if (true_label != (uint32_t)-1) { // JMPNZ_EX 8166 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 8167 | bvs => true_label 8168 | bne => true_label 8169 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 8170 } else if (set_bool_not) { // BOOL_NOT 8171 | mov REG0w, #IS_FALSE 8172 | bvs >1 8173 | bne >1 8174 | mov REG0w, #IS_TRUE 8175 |1: 8176 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 8177 } else { // BOOL 8178 | mov REG0w, #IS_TRUE 8179 | bvs >1 8180 | bne >1 8181 | mov REG0w, #IS_FALSE 8182 |1: 8183 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 8184 } 8185 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8186 | b >9 8187 |.code 8188 } 8189 } else { 8190 if (exit_addr) { 8191 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 8192 | bvs &exit_addr 8193 | bne &exit_addr 8194 |1: 8195 } else { 8196 | bvs >1 8197 | beq &exit_addr 8198 |1: 8199 } 8200 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8201 | b >9 8202 } 8203 } else { 8204 ZEND_ASSERT(true_label != (uint32_t)-1 || false_label != (uint32_t)-1); 8205 if (false_label != (uint32_t)-1 ) { 8206 | bvs >1 8207 | beq => false_label 8208 |1: 8209 if (true_label != (uint32_t)-1) { 8210 | b =>true_label 8211 } else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8212 | b >9 8213 } 8214 } else { 8215 | bvs => true_label 8216 | bne => true_label 8217 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8218 | b >9 8219 } 8220 } 8221 } 8222 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8223 |.code 8224 } 8225 } 8226 } else if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) { 8227 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8228 |.cold_code 8229 |2: 8230 } 8231 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 8232 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 8233 } 8234 | SET_EX_OPLINE opline, REG0 8235 | EXT_CALL zend_is_true, REG0 8236 | mov REG0, RETVALx 8237 8238 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 8239 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 8240 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); 8241 8242 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 8243 | IF_NOT_ZVAL_REFCOUNTED op1_addr, >3, ZREG_TMP1, ZREG_TMP2 8244 } 8245 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 8246 | GC_DELREF FCARG1x, TMP1w 8247 | bne >3 8248 // In x86, r0 is used in macro ZVAL_DTOR_FUNC as temporary register, hence, r0 should be saved/restored 8249 // before/after this macro. In AArch64, TMP1 is used, but we still have to store REG0, 8250 // because it's clobbered by function call. 8251 | str REG0, T1 // save 8252 | ZVAL_DTOR_FUNC op1_info, opline, TMP1 8253 | ldr REG0, T1 // restore 8254 |3: 8255 } 8256 if (may_throw) { 8257 | MEM_LOAD_64_ZTS ldr, REG1, executor_globals, exception, TMP1 8258 | cbnz REG1, ->exception_handler_undef 8259 } 8260 8261 if (set_bool) { 8262 if (set_bool_not) { 8263 | neg REG0w, REG0w 8264 | add REG0w, REG0w, #3 8265 } else { 8266 | add REG0w, REG0w, #2 8267 } 8268 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 8269 if (exit_addr) { 8270 | CMP_ZVAL_TYPE res_addr, IS_FALSE, ZREG_TMP1 8271 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 8272 | bne &exit_addr 8273 } else { 8274 | beq &exit_addr 8275 } 8276 } else if (true_label != (uint32_t)-1 || false_label != (uint32_t)-1) { 8277 | CMP_ZVAL_TYPE res_addr, IS_FALSE, ZREG_TMP1 8278 if (true_label != (uint32_t)-1) { 8279 | bne =>true_label 8280 if (false_label != (uint32_t)-1) { 8281 | b =>false_label 8282 } else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8283 | b >9 8284 } 8285 } else { 8286 | beq =>false_label 8287 } 8288 } 8289 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8290 | b >9 8291 |.code 8292 } 8293 } else { 8294 if (exit_addr) { 8295 if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { 8296 | cbnz REG0w, &exit_addr 8297 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8298 | b >9 8299 } 8300 } else { 8301 | cbz REG0w, &exit_addr 8302 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8303 | b >9 8304 } 8305 } 8306 } else if (true_label != (uint32_t)-1) { 8307 | cbnz REG0w, =>true_label 8308 if (false_label != (uint32_t)-1) { 8309 | b =>false_label 8310 } else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8311 | b >9 8312 } 8313 } else { 8314 | cbz REG0w, =>false_label 8315 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8316 | b >9 8317 } 8318 } 8319 8320 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { 8321 |.code 8322 } 8323 } 8324 } 8325 8326 |9: 8327 8328 return 1; 8329} 8330 8331static 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) 8332{ 8333 if (op1_addr != op1_def_addr) { 8334 if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) { 8335 return 0; 8336 } 8337 if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) { 8338 op1_addr = op1_def_addr; 8339 } 8340 } 8341 8342 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)) { 8343 return 0; 8344 } 8345 if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) { 8346 return 0; 8347 } 8348 if (op1_info & MAY_BE_UNDEF) { 8349 zend_jit_check_exception(Dst); 8350 } 8351 return 1; 8352} 8353 8354static 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) 8355{ 8356 ZEND_ASSERT(opline->op1_type == IS_CV); 8357 8358 if (op2_addr != op2_def_addr) { 8359 if (!zend_jit_update_regs(Dst, opline->op2.var, op2_addr, op2_def_addr, op2_info)) { 8360 return 0; 8361 } 8362 if (Z_MODE(op2_def_addr) == IS_REG && Z_MODE(op2_addr) != IS_REG) { 8363 op2_addr = op2_def_addr; 8364 } 8365 } 8366 8367 if (Z_MODE(op1_addr) != IS_REG 8368 && Z_MODE(op1_use_addr) == IS_REG 8369 && !Z_LOAD(op1_use_addr) 8370 && !Z_STORE(op1_use_addr)) { 8371 /* Force type update */ 8372 op1_info |= MAY_BE_UNDEF; 8373 } 8374 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, 8375 may_throw)) { 8376 return 0; 8377 } 8378 if (!zend_jit_store_var_if_necessary_ex(Dst, opline->op1.var, op1_addr, op1_def_info, op1_use_addr, op1_info)) { 8379 return 0; 8380 } 8381 if (opline->result_type != IS_UNUSED) { 8382 if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) { 8383 return 0; 8384 } 8385 } 8386 8387 return 1; 8388} 8389 8390/* copy of hidden zend_closure */ 8391typedef struct _zend_closure { 8392 zend_object std; 8393 zend_function func; 8394 zval this_ptr; 8395 zend_class_entry *called_scope; 8396 zif_handler orig_internal_handler; 8397} zend_closure; 8398 8399static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_t used_stack) 8400{ 8401 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 8402 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 8403 8404 if (!exit_addr) { 8405 return 0; 8406 } 8407 8408 | // Check Stack Overflow 8409 | MEM_LOAD_64_ZTS ldr, REG1, executor_globals, vm_stack_end, TMP1 8410 | MEM_LOAD_OP_ZTS sub, ldr, REG1, executor_globals, vm_stack_top, TMP1, TMP2 8411 | CMP_64_WITH_CONST_32 REG1, used_stack, TMP1 8412 | blo &exit_addr 8413 8414 return 1; 8415} 8416 8417static 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) 8418{ 8419 uint32_t used_stack; 8420 bool stack_check = 1; 8421 8422 // REG0 -> zend_function 8423 // FCARG1 -> used_stack 8424 8425 if (func) { 8426 used_stack = zend_vm_calc_used_stack(opline->extended_value, func); 8427 if ((int)used_stack <= checked_stack) { 8428 stack_check = 0; 8429 } 8430 } else { 8431 used_stack = (ZEND_CALL_FRAME_SLOT + opline->extended_value + ZEND_OBSERVER_ENABLED) * sizeof(zval); 8432 8433 | // if (EXPECTED(ZEND_USER_CODE(func->type))) { 8434 if (!is_closure) { 8435 | LOAD_32BIT_VAL FCARG1w, used_stack 8436 | // Check whether REG0 is an internal function. 8437 | ldrb TMP1w, [REG0, #offsetof(zend_function, type)] 8438 | TST_32_WITH_CONST TMP1w, 1, TMP2w 8439 | bne >1 8440 } else { 8441 | LOAD_32BIT_VAL FCARG1w, used_stack 8442 } 8443 | // used_stack += (func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args)) * sizeof(zval); 8444 | LOAD_32BIT_VAL REG2w, opline->extended_value 8445 if (!is_closure) { 8446 | ldr TMP1w, [REG0, #offsetof(zend_function, op_array.num_args)] 8447 | cmp REG2w, TMP1w 8448 | csel REG2w, REG2w, TMP1w, le 8449 | ldr TMP1w, [REG0, #offsetof(zend_function, op_array.last_var)] 8450 | sub REG2w, REG2w, TMP1w 8451 | ldr TMP1w, [REG0, #offsetof(zend_function, op_array.T)] 8452 | sub REG2w, REG2w, TMP1w 8453 } else { 8454 | ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.num_args)] 8455 | cmp REG2w, TMP1w 8456 | csel REG2w, REG2w, TMP1w, le 8457 | ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.last_var)] 8458 | sub REG2w, REG2w, TMP1w 8459 | ldr TMP1w, [REG0, #offsetof(zend_closure, func.op_array.T)] 8460 | sub REG2w, REG2w, TMP1w 8461 } 8462 | sxtw REG2, REG2w 8463 | sub FCARG1x, FCARG1x, REG2, lsl #4 8464 |1: 8465 } 8466 8467 zend_jit_start_reuse_ip(); 8468 8469 | // if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) { 8470 | MEM_LOAD_64_ZTS ldr, RX, executor_globals, vm_stack_top, TMP1 8471 8472 if (stack_check) { 8473 | // Check Stack Overflow 8474 | MEM_LOAD_64_ZTS ldr, REG2, executor_globals, vm_stack_end, TMP1 8475 | sub REG2, REG2, RX 8476 if (func) { 8477 | CMP_64_WITH_CONST_32 REG2, used_stack, TMP1 8478 } else { 8479 | cmp REG2, FCARG1x 8480 } 8481 8482 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 8483 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 8484 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 8485 8486 if (!exit_addr) { 8487 return 0; 8488 } 8489 8490 | blo &exit_addr 8491 } else { 8492 | blo >1 8493 | // EG(vm_stack_top) = (zval*)((char*)call + used_stack); 8494 |.cold_code 8495 |1: 8496 if (func) { 8497 | LOAD_32BIT_VAL FCARG1w, used_stack 8498 } 8499 if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) { 8500 | SET_EX_OPLINE opline, REG0 8501 | EXT_CALL zend_jit_int_extend_stack_helper, REG0 8502 } else { 8503 if (!is_closure) { 8504 | mov FCARG2x, REG0 8505 } else { 8506 | add FCARG2x, REG0, #offsetof(zend_closure, func) 8507 } 8508 | SET_EX_OPLINE opline, REG0 8509 | EXT_CALL zend_jit_extend_stack_helper, REG0 8510 } 8511 | mov RX, RETVALx 8512 | b >1 8513 |.code 8514 } 8515 } 8516 8517 if (func) { 8518 || if (arm64_may_encode_imm12((int64_t)used_stack)) { 8519 | MEM_UPDATE_ZTS add, ldr, str, #used_stack, executor_globals, vm_stack_top, REG2, TMP1 8520 || } else { 8521 | LOAD_32BIT_VAL TMP1w, used_stack 8522 | MEM_UPDATE_ZTS add, ldr, str, TMP1, executor_globals, vm_stack_top, REG2, TMP2 8523 || } 8524 } else { 8525 | MEM_UPDATE_ZTS add, ldr, str, FCARG1x, executor_globals, vm_stack_top, REG2, TMP1 8526 } 8527 | // zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object); 8528 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) { 8529 | // ZEND_SET_CALL_INFO(call, 0, call_info); 8530 | LOAD_32BIT_VAL TMP1w, (IS_UNDEF | ZEND_CALL_NESTED_FUNCTION) 8531 | str TMP1w, EX:RX->This.u1.type_info 8532 } 8533 if (opline->opcode == ZEND_INIT_FCALL && func && func->type == ZEND_INTERNAL_FUNCTION) { 8534 | // call->func = func; 8535 |1: 8536 | ADDR_STORE EX:RX->func, func, REG1 8537 } else { 8538 if (!is_closure) { 8539 | // call->func = func; 8540 | str REG0, EX:RX->func 8541 } else { 8542 | // call->func = &closure->func; 8543 | add REG1, REG0, #offsetof(zend_closure, func) 8544 | str REG1, EX:RX->func 8545 } 8546 |1: 8547 } 8548 if (opline->opcode == ZEND_INIT_METHOD_CALL) { 8549 | // Z_PTR(call->This) = obj; 8550 | ldr REG1, T1 8551 | str REG1, EX:RX->This.value.ptr 8552 if (opline->op1_type == IS_UNUSED || delayed_fetch_this) { 8553 | // call->call_info |= ZEND_CALL_HAS_THIS; 8554 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 8555 | LOAD_32BIT_VAL TMP1w, ZEND_CALL_HAS_THIS 8556 | str TMP1w, EX:RX->This.u1.type_info 8557 } else { 8558 | ldr TMP1w, EX:RX->This.u1.type_info 8559 | BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_HAS_THIS, TMP2w 8560 | str TMP1w, EX:RX->This.u1.type_info 8561 } 8562 } else { 8563 if (opline->op1_type == IS_CV) { 8564 | // GC_ADDREF(obj); 8565 | GC_ADDREF REG1, TMP1w 8566 } 8567 | // call->call_info |= ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS; 8568 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 8569 | LOAD_32BIT_VAL TMP1w, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS) 8570 | str TMP1w, EX:RX->This.u1.type_info 8571 } else { 8572 | ldr TMP1w, EX:RX->This.u1.type_info 8573 | BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, (ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS), TMP2w 8574 | str TMP1w, EX:RX->This.u1.type_info 8575 } 8576 } 8577 } else if (!is_closure) { 8578 | // Z_CE(call->This) = called_scope; 8579 | str xzr, EX:RX->This.value.ptr 8580 } else { 8581 if (opline->op2_type == IS_CV) { 8582 | // GC_ADDREF(closure); 8583 | GC_ADDREF REG0, TMP1w 8584 } 8585 | // object_or_called_scope = closure->called_scope; 8586 | ldr REG1, [REG0, #offsetof(zend_closure, called_scope)] 8587 | str REG1, EX:RX->This.value.ptr 8588 | // call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE | 8589 | // (closure->func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE); 8590 | ldr REG2w, [REG0, #offsetof(zend_closure, func.common.fn_flags)] 8591 | BW_OP_32_WITH_CONST and, REG2w, REG2w, ZEND_ACC_FAKE_CLOSURE, TMP1w 8592 | BW_OP_32_WITH_CONST orr, REG2w, REG2w, (ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE), TMP1w 8593 | // if (Z_TYPE(closure->this_ptr) != IS_UNDEF) { 8594 | ldrb TMP1w, [REG0, #offsetof(zend_closure, this_ptr.u1.v.type)] 8595 | cmp TMP1w, #IS_UNDEF 8596 | beq >1 8597 | // call_info |= ZEND_CALL_HAS_THIS; 8598 | BW_OP_32_WITH_CONST orr, REG2w, REG2w, ZEND_CALL_HAS_THIS, TMP1w 8599 | // object_or_called_scope = Z_OBJ(closure->this_ptr); 8600 | ldr REG1, [REG0, #offsetof(zend_closure, this_ptr.value.ptr)] 8601 |1: 8602 | // ZEND_SET_CALL_INFO(call, 0, call_info); 8603 | ldr TMP1w, EX:RX->This.u1.type_info 8604 | orr TMP1w, TMP1w, REG2w 8605 | str TMP1w, EX:RX->This.u1.type_info 8606 | // Z_PTR(call->This) = object_or_called_scope; 8607 | str REG1, EX:RX->This.value.ptr 8608 | ldr TMP1, [REG0, #offsetof(zend_closure, func.op_array.run_time_cache__ptr)] 8609 | cbnz TMP1, >1 8610 | add FCARG1x, REG0, #offsetof(zend_closure, func) 8611 | EXT_CALL zend_jit_init_func_run_time_cache_helper, REG0 8612 |1: 8613 } 8614 | // ZEND_CALL_NUM_ARGS(call) = num_args; 8615 | LOAD_32BIT_VAL TMP1w, opline->extended_value 8616 | str TMP1w, EX:RX->This.u2.num_args 8617 8618 return 1; 8619} 8620 8621static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zend_function *func, const zend_op *to_opline) 8622{ 8623 int32_t exit_point; 8624 const void *exit_addr; 8625 8626 if (func->type == ZEND_INTERNAL_FUNCTION) { 8627#ifdef ZEND_WIN32 8628 // TODO: ASLR may cause different addresses in different workers ??? 8629 return 0; 8630#endif 8631 } else if (func->type == ZEND_USER_FUNCTION) { 8632 if (!zend_accel_in_shm(func->op_array.opcodes)) { 8633 /* op_array and op_array->opcodes are not persistent. We can't link. */ 8634 return 0; 8635 } 8636 } else { 8637 ZEND_UNREACHABLE(); 8638 return 0; 8639 } 8640 8641 exit_point = zend_jit_trace_get_exit_point(to_opline, ZEND_JIT_EXIT_POLYMORPHISM); 8642 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 8643 if (!exit_addr) { 8644 return 0; 8645 } 8646 8647 | // call = EX(call); 8648 | ldr REG1, EX->call 8649 while (level > 0) { 8650 | ldr REG1, EX:REG1->prev_execute_data 8651 level--; 8652 } 8653 8654 if (func->type == ZEND_USER_FUNCTION && 8655 (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) || 8656 (func->common.fn_flags & ZEND_ACC_CLOSURE) || 8657 !func->common.function_name)) { 8658 const zend_op *opcodes = func->op_array.opcodes; 8659 8660 | ldr REG1, EX:REG1->func 8661 | LOAD_ADDR REG2, ((ptrdiff_t)opcodes) 8662 | ldr TMP1, [REG1, #offsetof(zend_op_array, opcodes)] 8663 | cmp TMP1, REG2 8664 | bne &exit_addr 8665 } else { 8666 | LOAD_ADDR REG2, ((ptrdiff_t)func) 8667 | ldr TMP1, EX:REG1->func 8668 | cmp TMP1, REG2 8669 | bne &exit_addr 8670 } 8671 8672 return 1; 8673} 8674 8675static 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) 8676{ 8677 zend_func_info *info = ZEND_FUNC_INFO(op_array); 8678 zend_call_info *call_info = NULL; 8679 zend_function *func = NULL; 8680 8681 if (delayed_call_chain) { 8682 if (!zend_jit_save_call_chain(Dst, delayed_call_level)) { 8683 return 0; 8684 } 8685 } 8686 8687 if (info) { 8688 call_info = info->callee_info; 8689 while (call_info && call_info->caller_init_opline != opline) { 8690 call_info = call_info->next_callee; 8691 } 8692 if (call_info && call_info->callee_func && !call_info->is_prototype) { 8693 func = call_info->callee_func; 8694 } 8695 } 8696 8697 if (!func 8698 && trace 8699 && trace->op == ZEND_JIT_TRACE_INIT_CALL) { 8700 func = (zend_function*)trace->func; 8701 } 8702 8703 if (opline->opcode == ZEND_INIT_FCALL 8704 && func 8705 && func->type == ZEND_INTERNAL_FUNCTION) { 8706 /* load constant address later */ 8707 } else if (func && op_array == &func->op_array) { 8708 /* recursive call */ 8709 | ldr REG0, EX->func 8710 } else { 8711 | // if (CACHED_PTR(opline->result.num)) 8712 | ldr REG2, EX->run_time_cache 8713 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG2, opline->result.num, TMP1 8714 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 8715 && func 8716 && (func->common.fn_flags & ZEND_ACC_IMMUTABLE) 8717 && opline->opcode != ZEND_INIT_FCALL) { 8718 /* Called func may be changed because of recompilation. See ext/opcache/tests/jit/init_fcall_003.phpt */ 8719 | LOAD_ADDR REG1, ((ptrdiff_t)func) 8720 | cmp REG0, REG1 8721 | bne >1 8722 } else { 8723 | cbz REG0, >1 8724 } 8725 |.cold_code 8726 |1: 8727 if (opline->opcode == ZEND_INIT_FCALL 8728 && func 8729 && func->type == ZEND_USER_FUNCTION 8730 && (func->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) { 8731 | LOAD_ADDR FCARG1x, func 8732 | MEM_ACCESS_64_WITH_UOFFSET str, FCARG1x, REG2, opline->result.num, TMP1 8733 | EXT_CALL zend_jit_init_func_run_time_cache_helper, REG0 8734 | mov REG0, RETVALx 8735 | b >3 8736 } else { 8737 zval *zv = RT_CONSTANT(opline, opline->op2); 8738 8739 if (opline->opcode == ZEND_INIT_FCALL) { 8740 | LOAD_ADDR FCARG1x, Z_STR_P(zv); 8741 | ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1 8742 | EXT_CALL zend_jit_find_func_helper, REG0 8743 } else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) { 8744 | LOAD_ADDR FCARG1x, Z_STR_P(zv + 1); 8745 | ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1 8746 | EXT_CALL zend_jit_find_func_helper, REG0 8747 } else if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { 8748 | LOAD_ADDR FCARG1x, zv; 8749 | ADD_SUB_64_WITH_CONST_32 add, FCARG2x, REG2, opline->result.num, TMP1 8750 | EXT_CALL zend_jit_find_ns_func_helper, REG0 8751 } else { 8752 ZEND_UNREACHABLE(); 8753 } 8754 | // Get the return value of function zend_jit_find_func_helper/zend_jit_find_ns_func_helper 8755 | mov REG0, RETVALx 8756 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 8757 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 8758 func && (func->common.fn_flags & ZEND_ACC_IMMUTABLE) ? ZEND_JIT_EXIT_INVALIDATE : 0); 8759 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 8760 8761 if (!exit_addr) { 8762 return 0; 8763 } 8764 8765 if (!func || opline->opcode == ZEND_INIT_FCALL) { 8766 | cbnz REG0, >3 8767 } else if (func->type == ZEND_USER_FUNCTION 8768 && !(func->common.fn_flags & ZEND_ACC_IMMUTABLE)) { 8769 const zend_op *opcodes = func->op_array.opcodes; 8770 8771 | LOAD_ADDR REG1, ((ptrdiff_t)opcodes) 8772 | ldr TMP1, [REG0, #offsetof(zend_op_array, opcodes)] 8773 | cmp TMP1, REG1 8774 | beq >3 8775 } else { 8776 | LOAD_ADDR REG1, ((ptrdiff_t)func) 8777 | cmp REG0, REG1 8778 | beq >3 8779 } 8780 | b &exit_addr 8781 } else { 8782 | cbnz REG0, >3 8783 | // SAVE_OPLINE(); 8784 | SET_EX_OPLINE opline, REG0 8785 | b ->undefined_function 8786 } 8787 } 8788 |.code 8789 |3: 8790 } 8791 8792 if (!zend_jit_push_call_frame(Dst, opline, op_array, func, 0, 0, checked_stack)) { 8793 return 0; 8794 } 8795 8796 if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) { 8797 if (!zend_jit_save_call_chain(Dst, call_level)) { 8798 return 0; 8799 } 8800 } else { 8801 delayed_call_chain = 1; 8802 delayed_call_level = call_level; 8803 } 8804 8805 return 1; 8806} 8807 8808static int zend_jit_init_method_call(dasm_State **Dst, 8809 const zend_op *opline, 8810 uint32_t b, 8811 const zend_op_array *op_array, 8812 zend_ssa *ssa, 8813 const zend_ssa_op *ssa_op, 8814 int call_level, 8815 uint32_t op1_info, 8816 zend_jit_addr op1_addr, 8817 zend_class_entry *ce, 8818 bool ce_is_instanceof, 8819 bool on_this, 8820 bool delayed_fetch_this, 8821 zend_class_entry *trace_ce, 8822 zend_jit_trace_rec *trace, 8823 int checked_stack, 8824 bool polymorphic_side_trace) 8825{ 8826 zend_func_info *info = ZEND_FUNC_INFO(op_array); 8827 zend_call_info *call_info = NULL; 8828 zend_function *func = NULL; 8829 zval *function_name; 8830 8831 ZEND_ASSERT(opline->op2_type == IS_CONST); 8832 ZEND_ASSERT(op1_info & MAY_BE_OBJECT); 8833 8834 function_name = RT_CONSTANT(opline, opline->op2); 8835 8836 if (info) { 8837 call_info = info->callee_info; 8838 while (call_info && call_info->caller_init_opline != opline) { 8839 call_info = call_info->next_callee; 8840 } 8841 if (call_info && call_info->callee_func && !call_info->is_prototype) { 8842 func = call_info->callee_func; 8843 } 8844 } 8845 8846 if (polymorphic_side_trace) { 8847 /* function is passed in r0 from parent_trace */ 8848 } else { 8849 if (on_this) { 8850 zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); 8851 8852 | GET_ZVAL_PTR FCARG1x, this_addr, TMP1 8853 } else { 8854 if (op1_info & MAY_BE_REF) { 8855 if (opline->op1_type == IS_CV) { 8856 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 8857 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 8858 } 8859 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 8860 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 8861 } else { 8862 /* Hack: Convert reference to regular value to simplify JIT code */ 8863 ZEND_ASSERT(Z_REG(op1_addr) == ZREG_FP); 8864 | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1 8865 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 8866 | EXT_CALL zend_jit_unref_helper, REG0 8867 |1: 8868 } 8869 } 8870 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) { 8871 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 8872 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 8873 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 8874 8875 if (!exit_addr) { 8876 return 0; 8877 } 8878 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1 8879 } else { 8880 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1 8881 |.cold_code 8882 |1: 8883 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 8884 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 8885 } 8886 | SET_EX_OPLINE opline, REG0 8887 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) { 8888 | EXT_CALL zend_jit_invalid_method_call_tmp, REG0 8889 } else { 8890 | EXT_CALL zend_jit_invalid_method_call, REG0 8891 } 8892 | b ->exception_handler 8893 |.code 8894 } 8895 } 8896 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 8897 } 8898 8899 if (delayed_call_chain) { 8900 if (!zend_jit_save_call_chain(Dst, delayed_call_level)) { 8901 return 0; 8902 } 8903 } 8904 8905 | str FCARG1x, T1 // save 8906 8907 if (func) { 8908 | // fbc = CACHED_PTR(opline->result.num + sizeof(void*)); 8909 | ldr REG0, EX->run_time_cache 8910 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->result.num + sizeof(void*)), TMP1 8911 | cbz REG0, >1 8912 } else { 8913 | // if (CACHED_PTR(opline->result.num) == obj->ce)) { 8914 | ldr REG0, EX->run_time_cache 8915 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, opline->result.num, TMP1 8916 | ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)] 8917 | cmp REG2, TMP1 8918 | bne >1 8919 | // fbc = CACHED_PTR(opline->result.num + sizeof(void*)); 8920 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->result.num + sizeof(void*)), TMP1 8921 } 8922 8923 |.cold_code 8924 |1: 8925 | LOAD_ADDR FCARG2x, function_name 8926 if (TMP_ZVAL_OFFSET == 0) { 8927 | mov CARG3, sp 8928 } else { 8929 | add CARG3, sp, #TMP_ZVAL_OFFSET 8930 } 8931 | SET_EX_OPLINE opline, REG0 8932 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) { 8933 | EXT_CALL zend_jit_find_method_tmp_helper, REG0 8934 } else { 8935 | EXT_CALL zend_jit_find_method_helper, REG0 8936 } 8937 | mov REG0, RETVALx 8938 | cbnz REG0, >2 8939 | b ->exception_handler 8940 |.code 8941 |2: 8942 } 8943 8944 if ((!func || zend_jit_may_be_modified(func, op_array)) 8945 && trace 8946 && trace->op == ZEND_JIT_TRACE_INIT_CALL 8947 && trace->func 8948 ) { 8949 int32_t exit_point; 8950 const void *exit_addr; 8951 8952 exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : ZEND_JIT_EXIT_METHOD_CALL); 8953 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 8954 if (!exit_addr) { 8955 return 0; 8956 } 8957 8958 func = (zend_function*)trace->func; 8959 8960 if (func->type == ZEND_USER_FUNCTION && 8961 (!(func->common.fn_flags & ZEND_ACC_IMMUTABLE) || 8962 (func->common.fn_flags & ZEND_ACC_CLOSURE) || 8963 !func->common.function_name)) { 8964 const zend_op *opcodes = func->op_array.opcodes; 8965 8966 | LOAD_ADDR TMP1, opcodes 8967 | ldr TMP2, [REG0, #offsetof(zend_op_array, opcodes)] 8968 | cmp TMP2, TMP1 8969 | bne &exit_addr 8970 } else { 8971 | LOAD_ADDR TMP1, func 8972 | cmp REG0, TMP1 8973 | bne &exit_addr 8974 } 8975 } 8976 8977 if (!func) { 8978 | // if (fbc->common.fn_flags & ZEND_ACC_STATIC) { 8979 | ldr TMP1w, [REG0, #offsetof(zend_function, common.fn_flags)] 8980 | TST_32_WITH_CONST TMP1w, ZEND_ACC_STATIC, TMP2w 8981 | bne >1 8982 |.cold_code 8983 |1: 8984 } 8985 8986 if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) != 0) { 8987 | ldr FCARG1x, T1 // restore 8988 | mov FCARG2x, REG0 8989 | LOAD_32BIT_VAL CARG3w, opline->extended_value 8990 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !delayed_fetch_this) { 8991 | EXT_CALL zend_jit_push_static_metod_call_frame_tmp, REG0 8992 } else { 8993 | EXT_CALL zend_jit_push_static_metod_call_frame, REG0 8994 } 8995 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR) && !delayed_fetch_this)) { 8996 | cbz RETVALx, ->exception_handler 8997 } 8998 | mov RX, RETVALx 8999 } 9000 9001 if (!func) { 9002 | b >9 9003 |.code 9004 } 9005 9006 if (!func || (func->common.fn_flags & ZEND_ACC_STATIC) == 0) { 9007 if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 0, delayed_fetch_this, checked_stack)) { 9008 return 0; 9009 } 9010 } 9011 9012 if (!func) { 9013 |9: 9014 } 9015 zend_jit_start_reuse_ip(); 9016 9017 if (zend_jit_needs_call_chain(call_info, b, op_array, ssa, ssa_op, opline, call_level, trace)) { 9018 if (!zend_jit_save_call_chain(Dst, call_level)) { 9019 return 0; 9020 } 9021 } else { 9022 delayed_call_chain = 1; 9023 delayed_call_level = call_level; 9024 } 9025 9026 return 1; 9027} 9028 9029static int zend_jit_init_closure_call(dasm_State **Dst, 9030 const zend_op *opline, 9031 uint32_t b, 9032 const zend_op_array *op_array, 9033 zend_ssa *ssa, 9034 const zend_ssa_op *ssa_op, 9035 int call_level, 9036 zend_jit_trace_rec *trace, 9037 int checked_stack) 9038{ 9039 zend_function *func = NULL; 9040 zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var); 9041 9042 | GET_ZVAL_PTR REG0, op2_addr, TMP1 9043 9044 if (ssa->var_info[ssa_op->op2_use].ce != zend_ce_closure 9045 && !(ssa->var_info[ssa_op->op2_use].type & MAY_BE_CLASS_GUARD)) { 9046 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 9047 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9048 9049 if (!exit_addr) { 9050 return 0; 9051 } 9052 9053 | LOAD_ADDR FCARG1x, ((ptrdiff_t)zend_ce_closure) 9054 | ldr, TMP1, [REG0, #offsetof(zend_object, ce)] 9055 | cmp TMP1, FCARG1x 9056 | bne &exit_addr 9057 if (ssa->var_info && ssa_op->op2_use >= 0) { 9058 ssa->var_info[ssa_op->op2_use].type |= MAY_BE_CLASS_GUARD; 9059 ssa->var_info[ssa_op->op2_use].ce = zend_ce_closure; 9060 ssa->var_info[ssa_op->op2_use].is_instanceof = 0; 9061 } 9062 } 9063 9064 if (trace 9065 && trace->op == ZEND_JIT_TRACE_INIT_CALL 9066 && trace->func 9067 && trace->func->type == ZEND_USER_FUNCTION) { 9068 const zend_op *opcodes; 9069 int32_t exit_point; 9070 const void *exit_addr; 9071 9072 func = (zend_function*)trace->func; 9073 opcodes = func->op_array.opcodes; 9074 exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_CLOSURE_CALL); 9075 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9076 if (!exit_addr) { 9077 return 0; 9078 } 9079 9080 | LOAD_ADDR FCARG1x, ((ptrdiff_t)opcodes) 9081 | ldr TMP1, [REG0, #offsetof(zend_closure, func.op_array.opcodes)] 9082 | cmp TMP1, FCARG1x 9083 | bne &exit_addr 9084 } 9085 9086 if (delayed_call_chain) { 9087 if (!zend_jit_save_call_chain(Dst, delayed_call_level)) { 9088 return 0; 9089 } 9090 } 9091 9092 if (!zend_jit_push_call_frame(Dst, opline, NULL, func, 1, 0, checked_stack)) { 9093 return 0; 9094 } 9095 9096 if (zend_jit_needs_call_chain(NULL, b, op_array, ssa, ssa_op, opline, call_level, trace)) { 9097 if (!zend_jit_save_call_chain(Dst, call_level)) { 9098 return 0; 9099 } 9100 } else { 9101 delayed_call_chain = 1; 9102 delayed_call_level = call_level; 9103 } 9104 9105 if (trace 9106 && trace->op == ZEND_JIT_TRACE_END 9107 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) { 9108 if (!zend_jit_set_valid_ip(Dst, opline + 1)) { 9109 return 0; 9110 } 9111 } 9112 9113 return 1; 9114} 9115 9116static 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) 9117{ 9118 zend_func_info *info = ZEND_FUNC_INFO(op_array); 9119 zend_call_info *call_info = NULL; 9120 const zend_function *func = NULL; 9121 uint32_t i; 9122 zend_jit_addr res_addr; 9123 uint32_t call_num_args = 0; 9124 bool unknown_num_args = 0; 9125 const void *exit_addr = NULL; 9126 const zend_op *prev_opline; 9127 9128 if (RETURN_VALUE_USED(opline)) { 9129 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 9130 } else { 9131 /* CPU stack allocated temporary zval */ 9132 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RSP, TMP_ZVAL_OFFSET); 9133 } 9134 9135 prev_opline = opline - 1; 9136 while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) { 9137 prev_opline--; 9138 } 9139 if (prev_opline->opcode == ZEND_SEND_UNPACK || prev_opline->opcode == ZEND_SEND_ARRAY || 9140 prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) { 9141 unknown_num_args = 1; 9142 } 9143 9144 if (info) { 9145 call_info = info->callee_info; 9146 while (call_info && call_info->caller_call_opline != opline) { 9147 call_info = call_info->next_callee; 9148 } 9149 if (call_info && call_info->callee_func && !call_info->is_prototype) { 9150 func = call_info->callee_func; 9151 } 9152 if ((op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) 9153 && JIT_G(current_frame) 9154 && JIT_G(current_frame)->call 9155 && !JIT_G(current_frame)->call->func) { 9156 call_info = NULL; func = NULL; /* megamorphic call from trait */ 9157 } 9158 } 9159 if (!func) { 9160 /* resolve function at run time */ 9161 } else if (func->type == ZEND_USER_FUNCTION) { 9162 ZEND_ASSERT(opline->opcode != ZEND_DO_ICALL); 9163 call_num_args = call_info->num_args; 9164 } else if (func->type == ZEND_INTERNAL_FUNCTION) { 9165 ZEND_ASSERT(opline->opcode != ZEND_DO_UCALL); 9166 call_num_args = call_info->num_args; 9167 } else { 9168 ZEND_UNREACHABLE(); 9169 } 9170 9171 if (trace && !func) { 9172 if (trace->op == ZEND_JIT_TRACE_DO_ICALL) { 9173 ZEND_ASSERT(trace->func->type == ZEND_INTERNAL_FUNCTION); 9174#ifndef ZEND_WIN32 9175 // TODO: ASLR may cause different addresses in different workers ??? 9176 func = trace->func; 9177 if (JIT_G(current_frame) && 9178 JIT_G(current_frame)->call && 9179 TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) { 9180 call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call); 9181 } else { 9182 unknown_num_args = 1; 9183 } 9184#endif 9185 } else if (trace->op == ZEND_JIT_TRACE_ENTER) { 9186 ZEND_ASSERT(trace->func->type == ZEND_USER_FUNCTION); 9187 if (zend_accel_in_shm(trace->func->op_array.opcodes)) { 9188 func = trace->func; 9189 if (JIT_G(current_frame) && 9190 JIT_G(current_frame)->call && 9191 TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call) >= 0) { 9192 call_num_args = TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)->call); 9193 } else { 9194 unknown_num_args = 1; 9195 } 9196 } 9197 } 9198 } 9199 9200 bool may_have_extra_named_params = 9201 opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS && 9202 (!func || func->common.fn_flags & ZEND_ACC_VARIADIC); 9203 9204 if (!reuse_ip) { 9205 zend_jit_start_reuse_ip(); 9206 | // call = EX(call); 9207 | ldr RX, EX->call 9208 } 9209 zend_jit_stop_reuse_ip(); 9210 9211 | // fbc = call->func; 9212 | // mov r2, EX:RX->func ??? 9213 | // SAVE_OPLINE(); 9214 | SET_EX_OPLINE opline, REG0 9215 9216 if (opline->opcode == ZEND_DO_FCALL) { 9217 if (!func) { 9218 if (trace) { 9219 uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 9220 9221 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9222 if (!exit_addr) { 9223 return 0; 9224 } 9225 | ldr REG0, EX:RX->func 9226 | ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)] 9227 | TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w 9228 | bne &exit_addr 9229 } 9230 } 9231 } 9232 9233 if (!delayed_call_chain) { 9234 if (call_level == 1) { 9235 | str xzr, EX->call 9236 } else { 9237 | //EX(call) = call->prev_execute_data; 9238 | ldr REG0, EX:RX->prev_execute_data 9239 | str REG0, EX->call 9240 } 9241 } 9242 delayed_call_chain = 0; 9243 9244 | //call->prev_execute_data = execute_data; 9245 | str EX, EX:RX->prev_execute_data 9246 9247 if (!func) { 9248 | ldr REG0, EX:RX->func 9249 } 9250 9251 if (opline->opcode == ZEND_DO_FCALL) { 9252 if (!func) { 9253 if (!trace) { 9254 | ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)] 9255 | TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w 9256 | bne >1 9257 |.cold_code 9258 |1: 9259 if (!GCC_GLOBAL_REGS) { 9260 | mov FCARG1x, RX 9261 } 9262 | EXT_CALL zend_jit_deprecated_helper, REG0 9263 | GET_LOW_8BITS RETVALw, RETVALw 9264 | ldr REG0, EX:RX->func // reload 9265 | cbnz RETVALw, >1 // Result is 0 on exception 9266 | b ->exception_handler 9267 |.code 9268 |1: 9269 } 9270 } else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) { 9271 if (!GCC_GLOBAL_REGS) { 9272 | mov FCARG1x, RX 9273 } 9274 | EXT_CALL zend_jit_deprecated_helper, REG0 9275 | cbz RETVALw, ->exception_handler 9276 } 9277 } 9278 9279 if (!func 9280 && opline->opcode != ZEND_DO_UCALL 9281 && opline->opcode != ZEND_DO_ICALL) { 9282 | ldrb TMP1w, [REG0, #offsetof(zend_function, type)] 9283 | cmp TMP1w, #ZEND_USER_FUNCTION 9284 | bne >8 9285 } 9286 9287 if ((!func || func->type == ZEND_USER_FUNCTION) 9288 && opline->opcode != ZEND_DO_ICALL) { 9289 | // EX(call) = NULL; 9290 | str xzr, EX:RX->call 9291 9292 if (RETURN_VALUE_USED(opline)) { 9293 | // EX(return_value) = EX_VAR(opline->result.var); 9294 | LOAD_ZVAL_ADDR REG2, res_addr 9295 | str REG2, EX:RX->return_value 9296 } else { 9297 | // EX(return_value) = 0; 9298 | str xzr, EX:RX->return_value 9299 } 9300 9301 //EX_LOAD_RUN_TIME_CACHE(op_array); 9302 if (!func || func->op_array.cache_size) { 9303 if (func && op_array == &func->op_array) { 9304 /* recursive call */ 9305 if (trace || func->op_array.cache_size > sizeof(void*)) { 9306 | ldr REG2, EX->run_time_cache 9307 | str REG2, EX:RX->run_time_cache 9308 } 9309 } else { 9310 if (func 9311 && !(func->op_array.fn_flags & ZEND_ACC_CLOSURE) 9312 && ZEND_MAP_PTR_IS_OFFSET(func->op_array.run_time_cache)) { 9313 | MEM_LOAD_64_ZTS ldr, REG2, compiler_globals, map_ptr_base, TMP1 9314 | ADD_SUB_64_WITH_CONST add, REG2, REG2, (uintptr_t)ZEND_MAP_PTR(func->op_array.run_time_cache), TMP1 9315 | ldr REG2, [REG2] 9316 } else if ((func && (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) || 9317 (JIT_G(current_frame) && 9318 JIT_G(current_frame)->call && 9319 TRACE_FRAME_IS_CLOSURE_CALL(JIT_G(current_frame)->call))) { 9320 /* Closures always use direct pointers */ 9321 | ldr REG0, EX:RX->func 9322 | ldr REG2, [REG0, #offsetof(zend_op_array, run_time_cache__ptr)] 9323 } else { 9324 if (func) { 9325 | ldr REG0, EX:RX->func 9326 } 9327 | ldr REG2, [REG0, #offsetof(zend_op_array, run_time_cache__ptr)] 9328 | TST_64_WITH_ONE REG2 9329 | beq >1 9330 | MEM_LOAD_OP_ZTS add, ldr, REG2, compiler_globals, map_ptr_base, REG1, TMP1 9331 | ldr REG2, [REG2] 9332 |1: 9333 } 9334 | str REG2, EX:RX->run_time_cache 9335 } 9336 } 9337 9338 | // EG(current_execute_data) = execute_data; 9339 | MEM_STORE_64_ZTS str, RX, executor_globals, current_execute_data, REG1 9340 | mov FP, RX 9341 9342 | // opline = op_array->opcodes; 9343 if (func && !unknown_num_args) { 9344 | ADD_SUB_64_WITH_CONST_32 add, TMP1, RX, (EX_NUM_TO_VAR(call_num_args) + offsetof(zval, u1.type_info)), TMP1 // induction variable 9345 for (i = call_num_args; i < func->op_array.last_var; i++) { 9346 | // ZVAL_UNDEF(EX_VAR(n)) 9347 | str wzr, [TMP1], #16 9348 } 9349 9350 if (call_num_args <= func->op_array.num_args) { 9351 if (!trace || (trace->op == ZEND_JIT_TRACE_END 9352 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) { 9353 uint32_t num_args; 9354 9355 if ((func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) { 9356 if (trace) { 9357 num_args = 0; 9358 } else if (call_info) { 9359 num_args = skip_valid_arguments(op_array, ssa, call_info); 9360 } else { 9361 num_args = call_num_args; 9362 } 9363 } else { 9364 num_args = call_num_args; 9365 } 9366 if (zend_accel_in_shm(func->op_array.opcodes)) { 9367 | LOAD_IP_ADDR (func->op_array.opcodes + num_args) 9368 } else { 9369 | ldr REG0, EX->func 9370 || ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(num_args * sizeof(zend_op)))); 9371 if (GCC_GLOBAL_REGS) { 9372 | ldr IP, [REG0, #offsetof(zend_op_array, opcodes)] 9373 if (num_args) { 9374 | add IP, IP, #(num_args * sizeof(zend_op)) 9375 } 9376 } else { 9377 | ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)] 9378 if (num_args) { 9379 | add FCARG1x, FCARG1x, #(num_args * sizeof(zend_op)) 9380 } 9381 | str FCARG1x, EX->opline 9382 } 9383 } 9384 9385 if (GCC_GLOBAL_REGS && !trace && op_array == &func->op_array 9386 && num_args >= op_array->required_num_args) { 9387 /* recursive call */ 9388 if (ZEND_OBSERVER_ENABLED) { 9389 | SAVE_IP 9390 | mov FCARG1x, FP 9391 | EXT_CALL zend_observer_fcall_begin, REG0 9392 } 9393#ifdef CONTEXT_THREADED_JIT 9394 | NIY // TODO 9395#else 9396 | b =>num_args 9397#endif 9398 return 1; 9399 } 9400 } 9401 } else { 9402 if (!trace || (trace->op == ZEND_JIT_TRACE_END 9403 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) { 9404 if (func && zend_accel_in_shm(func->op_array.opcodes)) { 9405 | LOAD_IP_ADDR (func->op_array.opcodes) 9406 } else if (GCC_GLOBAL_REGS) { 9407 | ldr IP, [REG0, #offsetof(zend_op_array, opcodes)] 9408 } else { 9409 | ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)] 9410 | str FCARG1x, EX->opline 9411 } 9412 } 9413 if (!GCC_GLOBAL_REGS) { 9414 | mov FCARG1x, FP 9415 } 9416 | EXT_CALL zend_jit_copy_extra_args_helper, REG0 9417 } 9418 } else { 9419 | // opline = op_array->opcodes 9420 if (func && zend_accel_in_shm(func->op_array.opcodes)) { 9421 | LOAD_IP_ADDR (func->op_array.opcodes) 9422 } else if (GCC_GLOBAL_REGS) { 9423 | ldr IP, [REG0, #offsetof(zend_op_array, opcodes)] 9424 } else { 9425 | ldr FCARG1x, [REG0, #offsetof(zend_op_array, opcodes)] 9426 | str FCARG1x, EX->opline 9427 } 9428 if (func) { 9429 | // num_args = EX_NUM_ARGS(); 9430 | ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)] 9431 | // if (UNEXPECTED(num_args > first_extra_arg)) 9432 | CMP_32_WITH_CONST REG1w, (func->op_array.num_args), TMP1w 9433 } else { 9434 | // first_extra_arg = op_array->num_args; 9435 | ldr REG2w, [REG0, #offsetof(zend_op_array, num_args)] 9436 | // num_args = EX_NUM_ARGS(); 9437 | ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)] 9438 | // if (UNEXPECTED(num_args > first_extra_arg)) 9439 | cmp REG1w, REG2w 9440 } 9441 | bgt >1 9442 |.cold_code 9443 |1: 9444 if (!GCC_GLOBAL_REGS) { 9445 | mov FCARG1x, FP 9446 } 9447 | EXT_CALL zend_jit_copy_extra_args_helper, REG0 9448 if (!func) { 9449 | ldr REG0, EX->func // reload 9450 } 9451 | ldr REG1w, [FP, #offsetof(zend_execute_data, This.u2.num_args)] // reload 9452 | b >1 9453 |.code 9454 if (!func || (func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0) { 9455 if (!func) { 9456 | // if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) 9457 | ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)] 9458 | TST_32_WITH_CONST TMP1w, ZEND_ACC_HAS_TYPE_HINTS, TMP2w 9459 | bne >1 9460 } 9461 | // opline += num_args; 9462 || ZEND_ASSERT(sizeof(zend_op) == 32); 9463 | mov REG2w, REG1w 9464 | ADD_IP_SHIFT REG2, lsl #5, TMP1 9465 } 9466 |1: 9467 | // if (EXPECTED((int)num_args < op_array->last_var)) { 9468 if (func) { 9469 | LOAD_32BIT_VAL REG2w, func->op_array.last_var 9470 } else { 9471 | ldr REG2w, [REG0, #offsetof(zend_op_array, last_var)] 9472 } 9473 | subs REG2w, REG2w, REG1w 9474 | ble >3 9475 | // zval *var = EX_VAR_NUM(num_args); 9476 | add REG1, FP, REG1, lsl #4 9477 || ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(ZEND_CALL_FRAME_SLOT * sizeof(zval)))); 9478 | add REG1, REG1, #(ZEND_CALL_FRAME_SLOT * sizeof(zval)) 9479 |2: 9480 | SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w 9481 | add REG1, REG1, #16 9482 | subs REG2w, REG2w, #1 9483 | bne <2 9484 |3: 9485 } 9486 9487 if (ZEND_OBSERVER_ENABLED) { 9488 if (trace && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) { 9489 ZEND_ASSERT(trace[1].op == ZEND_JIT_TRACE_VM || trace[1].op == ZEND_JIT_TRACE_END); 9490 | SET_EX_OPLINE trace[1].opline, REG0 9491 } else { 9492 | SAVE_IP 9493 } 9494 | mov FCARG1x, FP 9495 | EXT_CALL zend_observer_fcall_begin, REG0 9496 } 9497 9498 if (trace) { 9499 if (!func && (opline->opcode != ZEND_DO_UCALL)) { 9500 | b >9 9501 } 9502 } else { 9503#ifdef CONTEXT_THREADED_JIT 9504 | NIY // TODO: CONTEXT_THREADED_JIT is always undefined. 9505#else 9506 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 9507 | ADD_HYBRID_SPAD 9508 | JMP_IP TMP1 9509 } else if (GCC_GLOBAL_REGS) { 9510 | ldp x29, x30, [sp], # SPAD // stack alignment 9511 | JMP_IP TMP1 9512 } else { 9513 | ldp FP, RX, T2 // restore FP and IP 9514 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 9515 | mov RETVALx, #1 // ZEND_VM_ENTER 9516 | ret 9517 } 9518 } 9519#endif 9520 } 9521 9522 if ((!func || func->type == ZEND_INTERNAL_FUNCTION) 9523 && (opline->opcode != ZEND_DO_UCALL)) { 9524 if (!func && (opline->opcode != ZEND_DO_ICALL)) { 9525 |8: 9526 } 9527 if (opline->opcode == ZEND_DO_FCALL_BY_NAME) { 9528 if (!func) { 9529 if (trace) { 9530 uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 9531 9532 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9533 if (!exit_addr) { 9534 return 0; 9535 } 9536 | ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)] 9537 | TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w 9538 | bne &exit_addr 9539 } else { 9540 | ldr TMP1w, [REG0, #offsetof(zend_op_array, fn_flags)] 9541 | TST_32_WITH_CONST TMP1w, ZEND_ACC_DEPRECATED, TMP2w 9542 | bne >1 9543 |.cold_code 9544 |1: 9545 if (!GCC_GLOBAL_REGS) { 9546 | mov FCARG1x, RX 9547 } 9548 | EXT_CALL zend_jit_deprecated_helper, REG0 9549 | GET_LOW_8BITS RETVALw, RETVALw 9550 | ldr REG0, EX:RX->func // reload 9551 | cbnz RETVALw, >1 // Result is 0 on exception 9552 | b ->exception_handler 9553 |.code 9554 |1: 9555 } 9556 } else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) { 9557 if (!GCC_GLOBAL_REGS) { 9558 | mov FCARG1x, RX 9559 } 9560 | EXT_CALL zend_jit_deprecated_helper, REG0 9561 | cbz RETVALw, ->exception_handler 9562 | ldr REG0, EX:RX->func // reload 9563 } 9564 } 9565 9566 | // EG(current_execute_data) = execute_data; 9567 | MEM_STORE_64_ZTS str, RX, executor_globals, current_execute_data, REG1 9568 9569 if (ZEND_OBSERVER_ENABLED) { 9570 | mov FCARG1x, RX 9571 | EXT_CALL zend_observer_fcall_begin, REG0 9572 | ldr REG0, EX:RX->func // reload 9573 } 9574 9575 | // ZVAL_NULL(EX_VAR(opline->result.var)); 9576 | LOAD_ZVAL_ADDR FCARG2x, res_addr 9577 | SET_Z_TYPE_INFO FCARG2x, IS_NULL, TMP1w 9578 9579 zend_jit_reset_last_valid_opline(); 9580 9581 | // (zend_execute_internal ? zend_execute_internal : fbc->internal_function.handler)(call, ret); 9582 | mov FCARG1x, RX 9583 if (zend_execute_internal) { 9584 | EXT_CALL zend_execute_internal, REG0 9585 } else { 9586 if (func) { 9587 | EXT_CALL func->internal_function.handler, REG0 9588 } else { 9589 | ldr TMP1, [REG0, #offsetof(zend_internal_function, handler)] 9590 | blr TMP1 9591 } 9592 } 9593 9594 if (ZEND_OBSERVER_ENABLED) { 9595 | LOAD_ZVAL_ADDR FCARG2x, res_addr 9596 | mov FCARG1x, RX 9597 | EXT_CALL zend_observer_fcall_end, REG0 9598 } 9599 9600 | // EG(current_execute_data) = execute_data; 9601 | MEM_STORE_64_ZTS str, FP, executor_globals, current_execute_data, REG0 9602 9603 | // zend_vm_stack_free_args(call); 9604 if (func && !unknown_num_args) { 9605 for (i = 0; i < call_num_args; i++ ) { 9606 if (zend_jit_needs_arg_dtor(func, i, call_info)) { 9607 uint32_t offset = EX_NUM_TO_VAR(i); 9608 zend_jit_addr arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, offset); 9609 | ZVAL_PTR_DTOR arg_addr, (MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN), 0, 1, opline, ZREG_TMP1, ZREG_TMP2 9610 } 9611 } 9612 } else { 9613 | mov FCARG1x, RX 9614 | EXT_CALL zend_jit_vm_stack_free_args_helper, REG0 9615 } 9616 if (may_have_extra_named_params) { 9617 | ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 3)] 9618 | TST_32_WITH_CONST TMP1w, (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS >> 24), TMP2w 9619 | bne >1 9620 |.cold_code 9621 |1: 9622 | ldr FCARG1x, [RX, #offsetof(zend_execute_data, extra_named_params)] 9623 | EXT_CALL zend_free_extra_named_params, REG0 9624 | b >2 9625 |.code 9626 |2: 9627 } 9628 9629 |8: 9630 if (opline->opcode == ZEND_DO_FCALL) { 9631 // TODO: optimize ??? 9632 | // if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) 9633 | ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 2)] 9634 | TST_32_WITH_CONST TMP1w, (ZEND_CALL_RELEASE_THIS >> 16), TMP2w 9635 | bne >1 9636 |.cold_code 9637 |1: 9638 | add TMP1, RX, #offsetof(zend_execute_data, This) 9639 | GET_Z_PTR FCARG1x, TMP1 9640 | // OBJ_RELEASE(object); 9641 | OBJ_RELEASE ZREG_FCARG1, >2, ZREG_TMP1, ZREG_TMP2 9642 | b >2 9643 |.code 9644 |2: 9645 } 9646 9647 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 9648 !JIT_G(current_frame) || 9649 !JIT_G(current_frame)->call || 9650 !TRACE_FRAME_IS_NESTED(JIT_G(current_frame)->call) || 9651 prev_opline->opcode == ZEND_SEND_UNPACK || 9652 prev_opline->opcode == ZEND_SEND_ARRAY || 9653 prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS) { 9654 9655 | // zend_vm_stack_free_call_frame(call); 9656 | ldrb TMP1w, [RX, #(offsetof(zend_execute_data, This.u1.type_info) + 2)] 9657 | TST_32_WITH_CONST TMP1w, ((ZEND_CALL_ALLOCATED >> 16) & 0xff), TMP2w 9658 | bne >1 9659 |.cold_code 9660 |1: 9661 | mov FCARG1x, RX 9662 | EXT_CALL zend_jit_free_call_frame, REG0 9663 | b >1 9664 |.code 9665 } 9666 | MEM_STORE_64_ZTS str, RX, executor_globals, vm_stack_top, REG0 9667 |1: 9668 9669 if (!RETURN_VALUE_USED(opline)) { 9670 zend_class_entry *ce; 9671 bool ce_is_instanceof; 9672 uint32_t func_info = call_info ? 9673 zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof) : 9674 (MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN); 9675 9676 /* If an exception is thrown, the return_value may stay at the 9677 * original value of null. */ 9678 func_info |= MAY_BE_NULL; 9679 9680 if (func_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { 9681 | ZVAL_PTR_DTOR res_addr, func_info, 1, 1, opline, ZREG_TMP1, ZREG_TMP2 9682 } 9683 } 9684 9685 | // if (UNEXPECTED(EG(exception) != NULL)) { 9686 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 9687 | cbnz REG0, ->icall_throw_handler 9688 9689 // TODO: Can we avoid checking for interrupts after each call ??? 9690 if (trace && last_valid_opline != opline) { 9691 int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM); 9692 9693 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9694 if (!exit_addr) { 9695 return 0; 9696 } 9697 } else { 9698 exit_addr = NULL; 9699 } 9700 if (!zend_jit_check_timeout(Dst, opline + 1, exit_addr)) { 9701 return 0; 9702 } 9703 9704 if ((!trace || !func) && opline->opcode != ZEND_DO_ICALL) { 9705 | LOAD_IP_ADDR (opline + 1) 9706 } else if (trace 9707 && trace->op == ZEND_JIT_TRACE_END 9708 && trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) { 9709 | LOAD_IP_ADDR (opline + 1) 9710 } 9711 } 9712 9713 if (!func) { 9714 |9: 9715 } 9716 9717 return 1; 9718} 9719 9720static int zend_jit_send_val(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr) 9721{ 9722 uint32_t arg_num = opline->op2.num; 9723 zend_jit_addr arg_addr; 9724 9725 ZEND_ASSERT(opline->opcode == ZEND_SEND_VAL || arg_num <= MAX_ARG_FLAG_NUM); 9726 9727 if (!zend_jit_reuse_ip(Dst)) { 9728 return 0; 9729 } 9730 9731 if (opline->opcode == ZEND_SEND_VAL_EX) { 9732 uint32_t mask = ZEND_SEND_BY_REF << ((arg_num + 3) * 2); 9733 9734 ZEND_ASSERT(arg_num <= MAX_ARG_FLAG_NUM); 9735 9736 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 9737 && JIT_G(current_frame) 9738 && JIT_G(current_frame)->call 9739 && JIT_G(current_frame)->call->func) { 9740 if (ARG_MUST_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) { 9741 /* Don't generate code that always throws exception */ 9742 return 0; 9743 } 9744 } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 9745 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 9746 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9747 if (!exit_addr) { 9748 return 0; 9749 } 9750 | ldr REG0, EX:RX->func 9751 | ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)] 9752 | TST_32_WITH_CONST TMP1w, mask, TMP2w 9753 | bne &exit_addr 9754 } else { 9755 | ldr REG0, EX:RX->func 9756 | ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)] 9757 | TST_32_WITH_CONST TMP1w, mask, TMP2w 9758 | bne >1 9759 |.cold_code 9760 |1: 9761 if (Z_MODE(op1_addr) == IS_REG) { 9762 /* set type to avoid zval_ptr_dtor() on uninitialized value */ 9763 zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); 9764 | SET_ZVAL_TYPE_INFO addr, IS_UNDEF, TMP1w, TMP2 9765 } 9766 | SET_EX_OPLINE opline, REG0 9767 | b ->throw_cannot_pass_by_ref 9768 |.code 9769 } 9770 } 9771 9772 arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var); 9773 9774 if (opline->op1_type == IS_CONST) { 9775 zval *zv = RT_CONSTANT(opline, opline->op1); 9776 9777 | ZVAL_COPY_CONST arg_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0 9778 if (Z_REFCOUNTED_P(zv)) { 9779 | ADDREF_CONST zv, REG0, TMP1 9780 } 9781 } else { 9782 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 9783 } 9784 9785 return 1; 9786} 9787 9788static int zend_jit_check_undef_args(dasm_State **Dst, const zend_op *opline) 9789{ 9790 | ldr FCARG1x, EX->call 9791 | ldrb TMP1w, [FCARG1x, #(offsetof(zend_execute_data, This.u1.type_info) + 3)] 9792 | TST_32_WITH_CONST TMP1w, (ZEND_CALL_MAY_HAVE_UNDEF >> 24), TMP2w 9793 | bne >1 9794 |.cold_code 9795 |1: 9796 | SET_EX_OPLINE opline, REG0 9797 | EXT_CALL zend_handle_undef_args, REG0 9798 | cbz RETVALw, >2 9799 | b ->exception_handler 9800 |.code 9801 |2: 9802 9803 return 1; 9804} 9805 9806static int zend_jit_send_ref(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, int cold) 9807{ 9808 zend_jit_addr op1_addr, arg_addr, ref_addr; 9809 9810 op1_addr = OP1_ADDR(); 9811 arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var); 9812 9813 if (!zend_jit_reuse_ip(Dst)) { 9814 return 0; 9815 } 9816 9817 if (opline->op1_type == IS_VAR) { 9818 if (op1_info & MAY_BE_INDIRECT) { 9819 | LOAD_ZVAL_ADDR REG0, op1_addr 9820 | // if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) { 9821 | IF_NOT_Z_TYPE REG0, IS_INDIRECT, >1, TMP1w 9822 | // ret = Z_INDIRECT_P(ret); 9823 | GET_Z_PTR REG0, REG0 9824 |1: 9825 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 9826 } 9827 } else if (opline->op1_type == IS_CV) { 9828 if (op1_info & MAY_BE_UNDEF) { 9829 if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) { 9830 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 9831 | SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2 9832 | b >2 9833 |1: 9834 } 9835 op1_info &= ~MAY_BE_UNDEF; 9836 op1_info |= MAY_BE_NULL; 9837 } 9838 } else { 9839 ZEND_UNREACHABLE(); 9840 } 9841 9842 if (op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) { 9843 if (op1_info & MAY_BE_REF) { 9844 | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >2, ZREG_TMP1 9845 | GET_ZVAL_PTR REG1, op1_addr, TMP1 9846 | GC_ADDREF REG1, TMP1w 9847 | SET_ZVAL_PTR arg_addr, REG1, TMP1 9848 | SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX, TMP1w, TMP2 9849 | b >6 9850 } 9851 |2: 9852 | // ZVAL_NEW_REF(arg, varptr); 9853 if (opline->op1_type == IS_VAR) { 9854 if (Z_REG(op1_addr) != ZREG_REG0 || Z_OFFSET(op1_addr) != 0) { 9855 | LOAD_ZVAL_ADDR REG0, op1_addr 9856 } 9857 | str REG0, T1 // save 9858 } 9859 | EMALLOC sizeof(zend_reference), op_array, opline // Allocate space in REG0 9860 | mov TMP1w, #2 9861 | str TMP1w, [REG0] 9862 || ZEND_ASSERT(GC_REFERENCE <= MOVZ_IMM); 9863 | movz TMP1w, #GC_REFERENCE 9864 | str TMP1w, [REG0, #offsetof(zend_reference, gc.u.type_info)] 9865 | str xzr, [REG0, #offsetof(zend_reference, sources.ptr)] 9866 ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, offsetof(zend_reference, val)); 9867 if (opline->op1_type == IS_VAR) { 9868 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG1, 0); 9869 9870 | ldr REG1, T1 // restore 9871 | ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_REG2, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 9872 | SET_ZVAL_PTR val_addr, REG0, TMP1 9873 | SET_ZVAL_TYPE_INFO val_addr, IS_REFERENCE_EX, TMP1w, TMP2 9874 } else { 9875 | ZVAL_COPY_VALUE ref_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 9876 | SET_ZVAL_PTR op1_addr, REG0, TMP1 9877 | SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2 9878 } 9879 | SET_ZVAL_PTR arg_addr, REG0, TMP1 9880 | SET_ZVAL_TYPE_INFO arg_addr, IS_REFERENCE_EX, TMP1w, TMP2 9881 } 9882 9883 |6: 9884 | FREE_OP opline->op1_type, opline->op1, op1_info, !cold, opline, ZREG_TMP1, ZREG_TMP2 9885 |7: 9886 9887 return 1; 9888} 9889 9890static 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) 9891{ 9892 uint32_t arg_num = opline->op2.num; 9893 zend_jit_addr arg_addr; 9894 9895 ZEND_ASSERT((opline->opcode != ZEND_SEND_VAR_EX && 9896 opline->opcode != ZEND_SEND_VAR_NO_REF_EX) || 9897 arg_num <= MAX_ARG_FLAG_NUM); 9898 9899 arg_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, opline->result.var); 9900 9901 if (!zend_jit_reuse_ip(Dst)) { 9902 return 0; 9903 } 9904 9905 if (opline->opcode == ZEND_SEND_VAR_EX) { 9906 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 9907 && JIT_G(current_frame) 9908 && JIT_G(current_frame)->call 9909 && JIT_G(current_frame)->call->func) { 9910 if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) { 9911 if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) { 9912 return 0; 9913 } 9914 return 1; 9915 } 9916 } else { 9917 uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2); 9918 9919 | ldr REG0, EX:RX->func 9920 | ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)] 9921 | TST_32_WITH_CONST TMP1w, mask, TMP2w 9922 | bne >1 9923 |.cold_code 9924 |1: 9925 if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) { 9926 return 0; 9927 } 9928 | b >7 9929 |.code 9930 } 9931 } else if (opline->opcode == ZEND_SEND_VAR_NO_REF_EX) { 9932 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 9933 && JIT_G(current_frame) 9934 && JIT_G(current_frame)->call 9935 && JIT_G(current_frame)->call->func) { 9936 if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) { 9937 9938 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 9939 9940 if (!ARG_MAY_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) { 9941 if (!(op1_info & MAY_BE_REF)) { 9942 /* Don't generate code that always throws exception */ 9943 return 0; 9944 } else { 9945 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 9946 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9947 if (!exit_addr) { 9948 return 0; 9949 } 9950 | GET_LOW_8BITS TMP1w, REG1w 9951 | cmp TMP1w, #IS_REFERENCE 9952 | bne &exit_addr 9953 } 9954 } 9955 return 1; 9956 } 9957 } else { 9958 uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2); 9959 9960 | ldr REG0, EX:RX->func 9961 | ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)] 9962 | TST_32_WITH_CONST TMP1w, mask, TMP2w 9963 | bne >1 9964 |.cold_code 9965 |1: 9966 9967 mask = ZEND_SEND_PREFER_REF << ((arg_num + 3) * 2); 9968 9969 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 9970 if (op1_info & MAY_BE_REF) { 9971 | GET_LOW_8BITS TMP1w, REG1w 9972 | cmp TMP1w, #IS_REFERENCE 9973 | beq >7 9974 } 9975 | ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)] 9976 | TST_32_WITH_CONST TMP1w, mask, TMP2w 9977 | bne >7 9978 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 9979 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 9980 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 9981 if (!exit_addr) { 9982 return 0; 9983 } 9984 | b &exit_addr 9985 } else { 9986 | SET_EX_OPLINE opline, REG0 9987 | LOAD_ZVAL_ADDR FCARG1x, arg_addr 9988 | EXT_CALL zend_jit_only_vars_by_reference, REG0 9989 if (!zend_jit_check_exception(Dst)) { 9990 return 0; 9991 } 9992 | b >7 9993 } 9994 9995 |.code 9996 } 9997 } else if (opline->opcode == ZEND_SEND_FUNC_ARG) { 9998 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 9999 && JIT_G(current_frame) 10000 && JIT_G(current_frame)->call 10001 && JIT_G(current_frame)->call->func) { 10002 if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) { 10003 if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 0)) { 10004 return 0; 10005 } 10006 return 1; 10007 } 10008 } else { 10009 | ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10010 | TST_32_WITH_CONST TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w 10011 | bne >1 10012 |.cold_code 10013 |1: 10014 if (!zend_jit_send_ref(Dst, opline, op_array, op1_info, 1)) { 10015 return 0; 10016 } 10017 | b >7 10018 |.code 10019 } 10020 } 10021 10022 if (op1_info & MAY_BE_UNDEF) { 10023 if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) { 10024 | IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 10025 |.cold_code 10026 |1: 10027 } 10028 10029 | SET_EX_OPLINE opline, REG0 10030 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 10031 | EXT_CALL zend_jit_undefined_op_helper, REG0 10032 | SET_ZVAL_TYPE_INFO arg_addr, IS_NULL, TMP1w, TMP2 10033 | cbz RETVALx, ->exception_handler 10034 10035 if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) { 10036 | b >7 10037 |.code 10038 } else { 10039 |7: 10040 return 1; 10041 } 10042 } 10043 10044 if (opline->opcode == ZEND_SEND_VAR_NO_REF) { 10045 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10046 if (op1_info & MAY_BE_REF) { 10047 | GET_LOW_8BITS TMP1w, REG1w 10048 | cmp TMP1w, #IS_REFERENCE 10049 | beq >7 10050 } 10051 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 10052 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 10053 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 10054 if (!exit_addr) { 10055 return 0; 10056 } 10057 | b &exit_addr 10058 } else { 10059 | SET_EX_OPLINE opline, REG0 10060 | LOAD_ZVAL_ADDR FCARG1x, arg_addr 10061 | EXT_CALL zend_jit_only_vars_by_reference, REG0 10062 if (!zend_jit_check_exception(Dst)) { 10063 return 0; 10064 } 10065 } 10066 } else { 10067 if (op1_info & MAY_BE_REF) { 10068 if (opline->op1_type == IS_CV) { 10069 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 10070 10071 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 10072 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 10073 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, val_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10074 | TRY_ADDREF op1_info, REG0w, REG2, TMP1w 10075 } else { 10076 zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 8); 10077 10078 | IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1 10079 |.cold_code 10080 |1: 10081 | // zend_refcounted *ref = Z_COUNTED_P(retval_ptr); 10082 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 10083 | // ZVAL_COPY_VALUE(return_value, &ref->value); 10084 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10085 | GC_DELREF FCARG1x, TMP1w 10086 | beq >1 10087 | IF_NOT_REFCOUNTED REG0w, >2, TMP1w 10088 | GC_ADDREF REG2, TMP1w 10089 | b >2 10090 |1: 10091 | EFREE_REFERENCE 10092 | b >2 10093 |.code 10094 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10095 |2: 10096 } 10097 } else { 10098 if (op1_addr != op1_def_addr) { 10099 if (!zend_jit_update_regs(Dst, opline->op1.var, op1_addr, op1_def_addr, op1_info)) { 10100 return 0; 10101 } 10102 if (Z_MODE(op1_def_addr) == IS_REG && Z_MODE(op1_addr) != IS_REG) { 10103 op1_addr= op1_def_addr; 10104 } 10105 } 10106 | ZVAL_COPY_VALUE arg_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10107 if (opline->op1_type == IS_CV) { 10108 | TRY_ADDREF op1_info, REG0w, REG2, TMP1w 10109 } 10110 } 10111 } 10112 |7: 10113 10114 return 1; 10115} 10116 10117static int zend_jit_check_func_arg(dasm_State **Dst, const zend_op *opline) 10118{ 10119 uint32_t arg_num = opline->op2.num; 10120 10121 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 10122 && JIT_G(current_frame) 10123 && JIT_G(current_frame)->call 10124 && JIT_G(current_frame)->call->func) { 10125 if (ARG_SHOULD_BE_SENT_BY_REF(JIT_G(current_frame)->call->func, arg_num)) { 10126 if (!TRACE_FRAME_IS_LAST_SEND_BY_REF(JIT_G(current_frame)->call)) { 10127 TRACE_FRAME_SET_LAST_SEND_BY_REF(JIT_G(current_frame)->call); 10128 | // ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF); 10129 || if (reuse_ip) { 10130 | ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10131 | BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w 10132 | str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10133 || } else { 10134 | ldr REG0, EX->call 10135 | ldr TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)] 10136 | BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w 10137 | str TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)] 10138 || } 10139 } 10140 } else { 10141 if (!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { 10142 TRACE_FRAME_SET_LAST_SEND_BY_VAL(JIT_G(current_frame)->call); 10143 | // ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF); 10144 || if (reuse_ip) { 10145 | ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10146 | BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~ZEND_CALL_SEND_ARG_BY_REF), TMP2w 10147 | str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10148 || } else { 10149 | ldr REG0, EX->call 10150 | ldr TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)] 10151 | BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~ZEND_CALL_SEND_ARG_BY_REF), TMP2w 10152 | str TMP1w, [REG0, #offsetof(zend_execute_data, This.u1.type_info)] 10153 || } 10154 } 10155 } 10156 } else { 10157 // if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { 10158 uint32_t mask = (ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF) << ((arg_num + 3) * 2); 10159 10160 if (!zend_jit_reuse_ip(Dst)) { 10161 return 0; 10162 } 10163 10164 | ldr REG0, EX:RX->func 10165 | ldr TMP1w, [REG0, #offsetof(zend_function, quick_arg_flags)] 10166 | TST_32_WITH_CONST TMP1w, mask, TMP2w 10167 | bne >1 10168 |.cold_code 10169 |1: 10170 | // ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF); 10171 | ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10172 | BW_OP_32_WITH_CONST orr, TMP1w, TMP1w, ZEND_CALL_SEND_ARG_BY_REF, TMP2w 10173 | str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10174 | b >1 10175 |.code 10176 | // ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF); 10177 | ldr TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10178 | BW_OP_32_WITH_CONST and, TMP1w, TMP1w, (~(ZEND_CALL_SEND_ARG_BY_REF)), TMP2w 10179 | str TMP1w, [RX, #offsetof(zend_execute_data, This.u1.type_info)] 10180 |1: 10181 } 10182 10183 return 1; 10184} 10185 10186static 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) 10187{ 10188 if (smart_branch_opcode) { 10189 if (smart_branch_opcode == ZEND_JMPZ) { 10190 if (jmp) { 10191 | b >7 10192 } 10193 } else if (smart_branch_opcode == ZEND_JMPNZ) { 10194 | b =>target_label 10195 } else { 10196 ZEND_UNREACHABLE(); 10197 } 10198 } else { 10199 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 10200 10201 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 10202 if (jmp) { 10203 | b >7 10204 } 10205 } 10206 10207 return 1; 10208} 10209 10210static int zend_jit_smart_false(dasm_State **Dst, const zend_op *opline, int jmp, zend_uchar smart_branch_opcode, uint32_t target_label) 10211{ 10212 if (smart_branch_opcode) { 10213 if (smart_branch_opcode == ZEND_JMPZ) { 10214 | b =>target_label 10215 } else if (smart_branch_opcode == ZEND_JMPNZ) { 10216 if (jmp) { 10217 | b >7 10218 } 10219 } else { 10220 ZEND_UNREACHABLE(); 10221 } 10222 } else { 10223 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 10224 10225 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 10226 if (jmp) { 10227 | b >7 10228 } 10229 } 10230 10231 return 1; 10232} 10233 10234static 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) 10235{ 10236 uint32_t defined_label = (uint32_t)-1; 10237 uint32_t undefined_label = (uint32_t)-1; 10238 zval *zv = RT_CONSTANT(opline, opline->op1); 10239 zend_jit_addr res_addr = 0; 10240 10241 if (smart_branch_opcode && !exit_addr) { 10242 if (smart_branch_opcode == ZEND_JMPZ) { 10243 undefined_label = target_label; 10244 } else if (smart_branch_opcode == ZEND_JMPNZ) { 10245 defined_label = target_label; 10246 } else { 10247 ZEND_UNREACHABLE(); 10248 } 10249 } 10250 10251 | // if (CACHED_PTR(opline->extended_value)) { 10252 | ldr REG0, EX->run_time_cache 10253 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, opline->extended_value, TMP1 10254 | cbz REG0, >1 10255 | TST_64_WITH_ONE REG0 10256 | bne >4 10257 |.cold_code 10258 |4: 10259 | MEM_LOAD_64_ZTS ldr, FCARG1x, executor_globals, zend_constants, FCARG1x 10260 | ldr TMP1w, [FCARG1x, #offsetof(HashTable, nNumOfElements)] 10261 | cmp TMP1, REG0, lsr #1 10262 10263 if (smart_branch_opcode) { 10264 if (exit_addr) { 10265 if (smart_branch_opcode == ZEND_JMPZ) { 10266 | beq &exit_addr 10267 } else { 10268 | beq >3 10269 } 10270 } else if (undefined_label != (uint32_t)-1) { 10271 | beq =>undefined_label 10272 } else { 10273 | beq >3 10274 } 10275 } else { 10276 | beq >2 10277 } 10278 |1: 10279 | SET_EX_OPLINE opline, REG0 10280 | LOAD_ADDR FCARG1x, zv 10281 | EXT_CALL zend_jit_check_constant, REG0 10282 if (exit_addr) { 10283 if (smart_branch_opcode == ZEND_JMPNZ) { 10284 | cbz RETVALx, >3 10285 } else { 10286 | cbnz RETVALx, >3 10287 } 10288 | b &exit_addr 10289 } else if (smart_branch_opcode) { 10290 if (undefined_label != (uint32_t)-1) { 10291 | cbz RETVALx, =>undefined_label 10292 } else { 10293 | cbz RETVALx, >3 10294 } 10295 if (defined_label != (uint32_t)-1) { 10296 | b =>defined_label 10297 } else { 10298 | b >3 10299 } 10300 } else { 10301 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 10302 | cbnz RETVALx, >1 10303 |2: 10304 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 10305 | b >3 10306 } 10307 |.code 10308 if (smart_branch_opcode) { 10309 if (exit_addr) { 10310 if (smart_branch_opcode == ZEND_JMPNZ) { 10311 | b &exit_addr 10312 } 10313 } else if (defined_label != (uint32_t)-1) { 10314 | b =>defined_label 10315 } 10316 } else { 10317 |1: 10318 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 10319 } 10320 |3: 10321 10322 return 1; 10323} 10324 10325static 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) 10326{ 10327 uint32_t mask; 10328 zend_jit_addr op1_addr = OP1_ADDR(); 10329 10330 // TODO: support for is_resource() ??? 10331 ZEND_ASSERT(opline->extended_value != MAY_BE_RESOURCE); 10332 10333 if (op1_info & MAY_BE_UNDEF) { 10334 if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) { 10335 | IF_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 10336 |.cold_code 10337 |1: 10338 } 10339 | SET_EX_OPLINE opline, REG0 10340 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 10341 | EXT_CALL zend_jit_undefined_op_helper, REG0 10342 zend_jit_check_exception_undef_result(Dst, opline); 10343 if (opline->extended_value & MAY_BE_NULL) { 10344 if (exit_addr) { 10345 if (smart_branch_opcode == ZEND_JMPNZ) { 10346 | b &exit_addr 10347 } else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) { 10348 | b >7 10349 } 10350 } else if (!zend_jit_smart_true(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label, target_label2)) { 10351 return 0; 10352 } 10353 } else { 10354 if (exit_addr) { 10355 if (smart_branch_opcode == ZEND_JMPZ) { 10356 | b &exit_addr 10357 } else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0) { 10358 | b >7 10359 } 10360 } else if (!zend_jit_smart_false(Dst, opline, (op1_info & (MAY_BE_ANY|MAY_BE_REF)) != 0, smart_branch_opcode, target_label)) { 10361 return 0; 10362 } 10363 } 10364 if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) { 10365 |.code 10366 } 10367 } 10368 10369 if (op1_info & (MAY_BE_ANY|MAY_BE_REF)) { 10370 mask = opline->extended_value; 10371 if (!(op1_info & MAY_BE_GUARD) && !(op1_info & (MAY_BE_ANY - mask))) { 10372 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 10373 if (exit_addr) { 10374 if (smart_branch_opcode == ZEND_JMPNZ) { 10375 | b &exit_addr 10376 } 10377 } else if (!zend_jit_smart_true(Dst, opline, 0, smart_branch_opcode, target_label, target_label2)) { 10378 return 0; 10379 } 10380 } else if (!(op1_info & MAY_BE_GUARD) && !(op1_info & mask)) { 10381 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 10382 if (exit_addr) { 10383 if (smart_branch_opcode == ZEND_JMPZ) { 10384 | b &exit_addr 10385 } 10386 } else if (!zend_jit_smart_false(Dst, opline, 0, smart_branch_opcode, target_label)) { 10387 return 0; 10388 } 10389 } else { 10390 bool invert = 0; 10391 zend_uchar type; 10392 10393 switch (mask) { 10394 case MAY_BE_NULL: type = IS_NULL; break; 10395 case MAY_BE_FALSE: type = IS_FALSE; break; 10396 case MAY_BE_TRUE: type = IS_TRUE; break; 10397 case MAY_BE_LONG: type = IS_LONG; break; 10398 case MAY_BE_DOUBLE: type = IS_DOUBLE; break; 10399 case MAY_BE_STRING: type = IS_STRING; break; 10400 case MAY_BE_ARRAY: type = IS_ARRAY; break; 10401 case MAY_BE_OBJECT: type = IS_OBJECT; break; 10402 case MAY_BE_ANY - MAY_BE_NULL: type = IS_NULL; invert = 1; break; 10403 case MAY_BE_ANY - MAY_BE_FALSE: type = IS_FALSE; invert = 1; break; 10404 case MAY_BE_ANY - MAY_BE_TRUE: type = IS_TRUE; invert = 1; break; 10405 case MAY_BE_ANY - MAY_BE_LONG: type = IS_LONG; invert = 1; break; 10406 case MAY_BE_ANY - MAY_BE_DOUBLE: type = IS_DOUBLE; invert = 1; break; 10407 case MAY_BE_ANY - MAY_BE_STRING: type = IS_STRING; invert = 1; break; 10408 case MAY_BE_ANY - MAY_BE_ARRAY: type = IS_ARRAY; invert = 1; break; 10409 case MAY_BE_ANY - MAY_BE_OBJECT: type = IS_OBJECT; invert = 1; break; 10410 case MAY_BE_ANY - MAY_BE_RESOURCE: type = IS_OBJECT; invert = 1; break; 10411 default: 10412 type = 0; 10413 } 10414 10415 if (op1_info & MAY_BE_REF) { 10416 | LOAD_ZVAL_ADDR REG0, op1_addr 10417 | ZVAL_DEREF REG0, op1_info, TMP1w 10418 } 10419 if (type == 0) { 10420 if (smart_branch_opcode && 10421 (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 10422 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10423 if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10424 | // if (Z_REFCOUNTED_P(cv)) { 10425 | IF_ZVAL_REFCOUNTED op1_addr, >1, ZREG_TMP1, ZREG_TMP2 10426 |.cold_code 10427 |1: 10428 } 10429 | // if (!Z_DELREF_P(cv)) { 10430 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 10431 | GC_DELREF FCARG1x, TMP1w 10432 if (RC_MAY_BE_1(op1_info)) { 10433 if (RC_MAY_BE_N(op1_info)) { 10434 | bne >3 10435 } 10436 if (op1_info & MAY_BE_REF) { 10437 | ldrb REG0w, [REG0, #offsetof(zval,u1.v.type)] 10438 } else { 10439 | MEM_ACCESS_8_WITH_UOFFSET ldrb, REG0w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1 10440 } 10441 | str REG0w, T1 // save 10442 | // zval_dtor_func(r); 10443 | ZVAL_DTOR_FUNC op1_info, opline, TMP1 10444 | ldr REG1w, T1 // restore 10445 | b >2 10446 } 10447 if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10448 if (!RC_MAY_BE_1(op1_info)) { 10449 | b >3 10450 } 10451 |.code 10452 } 10453 |3: 10454 if (op1_info & MAY_BE_REF) { 10455 | ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)] 10456 } else { 10457 | MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1 10458 } 10459 |2: 10460 } else { 10461 if (op1_info & MAY_BE_REF) { 10462 | ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)] 10463 } else { 10464 | MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1 10465 } 10466 } 10467 | mov REG0w, #1 10468 | lsl REG0w, REG0w, REG1w 10469 | TST_32_WITH_CONST REG0w, mask, TMP1w 10470 if (exit_addr) { 10471 if (smart_branch_opcode == ZEND_JMPNZ) { 10472 | bne &exit_addr 10473 } else { 10474 | beq &exit_addr 10475 } 10476 } else if (smart_branch_opcode) { 10477 if (smart_branch_opcode == ZEND_JMPZ) { 10478 | beq =>target_label 10479 } else if (smart_branch_opcode == ZEND_JMPNZ) { 10480 | bne =>target_label 10481 } else { 10482 ZEND_UNREACHABLE(); 10483 } 10484 } else { 10485 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 10486 10487 | cset REG0w, ne 10488 | add REG0w, REG0w, #2 10489 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 10490 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 10491 } 10492 } else { 10493 if (smart_branch_opcode && 10494 (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 10495 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10496 if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10497 | // if (Z_REFCOUNTED_P(cv)) { 10498 | IF_ZVAL_REFCOUNTED op1_addr, >1, ZREG_TMP1, ZREG_TMP2 10499 |.cold_code 10500 |1: 10501 } 10502 | // if (!Z_DELREF_P(cv)) { 10503 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 10504 | GC_DELREF FCARG1x, TMP1w 10505 if (RC_MAY_BE_1(op1_info)) { 10506 if (RC_MAY_BE_N(op1_info)) { 10507 | bne >3 10508 } 10509 if (op1_info & MAY_BE_REF) { 10510 | ldrb REG0w, [REG0, #offsetof(zval,u1.v.type)] 10511 } else { 10512 | MEM_ACCESS_8_WITH_UOFFSET ldrb, REG0w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1 10513 } 10514 | str REG0w, T1 // save 10515 | // zval_dtor_func(r); 10516 | ZVAL_DTOR_FUNC op1_info, opline, TMP1 10517 | ldr REG1w, T1 // restore 10518 | b >2 10519 } 10520 if ((op1_info) & (MAY_BE_ANY-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10521 if (!RC_MAY_BE_1(op1_info)) { 10522 | b >3 10523 } 10524 |.code 10525 } 10526 |3: 10527 if (op1_info & MAY_BE_REF) { 10528 | ldrb REG1w, [REG0, #offsetof(zval,u1.v.type)] 10529 } else { 10530 | MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1 10531 } 10532 |2: 10533 // Note: 'type' is of uchar type and holds a positive value, 10534 // hence it's safe to directly encode it as the imm field of 'cmp' instruction. 10535 | cmp REG1w, #type 10536 } else { 10537 if (op1_info & MAY_BE_REF) { 10538 | ldrb TMP1w, [REG0, #offsetof(zval,u1.v.type)] 10539 | cmp TMP1w, #type 10540 } else { 10541 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP1w, FP, (opline->op1.var + offsetof(zval,u1.v.type)), TMP1 10542 | cmp TMP1w, #type 10543 } 10544 } 10545 if (exit_addr) { 10546 if (invert) { 10547 if (smart_branch_opcode == ZEND_JMPNZ) { 10548 | bne &exit_addr 10549 } else { 10550 | beq &exit_addr 10551 } 10552 } else { 10553 if (smart_branch_opcode == ZEND_JMPNZ) { 10554 | beq &exit_addr 10555 } else { 10556 | bne &exit_addr 10557 } 10558 } 10559 } else if (smart_branch_opcode) { 10560 if (invert) { 10561 if (smart_branch_opcode == ZEND_JMPZ) { 10562 | beq =>target_label 10563 } else if (smart_branch_opcode == ZEND_JMPNZ) { 10564 | bne =>target_label 10565 } else { 10566 ZEND_UNREACHABLE(); 10567 } 10568 } else { 10569 if (smart_branch_opcode == ZEND_JMPZ) { 10570 | bne =>target_label 10571 } else if (smart_branch_opcode == ZEND_JMPNZ) { 10572 | beq =>target_label 10573 } else { 10574 ZEND_UNREACHABLE(); 10575 } 10576 } 10577 } else { 10578 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 10579 10580 if (invert) { 10581 | cset REG0w, ne 10582 } else { 10583 | cset REG0w, eq 10584 } 10585 | add REG0w, REG0w, #2 10586 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 10587 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 10588 } 10589 } 10590 } 10591 } 10592 10593 |7: 10594 10595 return 1; 10596} 10597 10598static int zend_jit_leave_frame(dasm_State **Dst) 10599{ 10600 | // EG(current_execute_data) = EX(prev_execute_data); 10601 | ldr REG0, EX->prev_execute_data 10602 | MEM_STORE_64_ZTS str, REG0, executor_globals, current_execute_data, REG2 10603 return 1; 10604} 10605 10606static int zend_jit_free_cvs(dasm_State **Dst) 10607{ 10608 | // EG(current_execute_data) = EX(prev_execute_data); 10609 | ldr FCARG1x, EX->prev_execute_data 10610 | MEM_STORE_64_ZTS str, FCARG1x, executor_globals, current_execute_data, REG0 10611 | // zend_free_compiled_variables(execute_data); 10612 | mov FCARG1x, FP 10613 | EXT_CALL zend_free_compiled_variables, REG0 10614 return 1; 10615} 10616 10617static int zend_jit_free_cv(dasm_State **Dst, uint32_t info, uint32_t var) 10618{ 10619 if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { 10620 uint32_t offset = EX_NUM_TO_VAR(var); 10621 zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offset); 10622 | ZVAL_PTR_DTOR addr, info, 1, 1, NULL, ZREG_TMP1, ZREG_TMP2 10623 } 10624 return 1; 10625} 10626 10627static int zend_jit_free_op(dasm_State **Dst, const zend_op *opline, uint32_t info, uint32_t var_offset) 10628{ 10629 if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { 10630 zend_jit_addr addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var_offset); 10631 | ZVAL_PTR_DTOR addr, info, 0, 1, opline, ZREG_TMP1, ZREG_TMP2 10632 } 10633 return 1; 10634} 10635 10636static int zend_jit_leave_func(dasm_State **Dst, 10637 const zend_op_array *op_array, 10638 const zend_op *opline, 10639 uint32_t op1_info, 10640 bool left_frame, 10641 zend_jit_trace_rec *trace, 10642 zend_jit_trace_info *trace_info, 10643 int indirect_var_access, 10644 int may_throw) 10645{ 10646 bool may_be_top_frame = 10647 JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 10648 !JIT_G(current_frame) || 10649 !TRACE_FRAME_IS_NESTED(JIT_G(current_frame)); 10650 bool may_need_call_helper = 10651 indirect_var_access || /* may have symbol table */ 10652 !op_array->function_name || /* may have symbol table */ 10653 may_be_top_frame || 10654 (op_array->fn_flags & ZEND_ACC_VARIADIC) || /* may have extra named args */ 10655 JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 10656 !JIT_G(current_frame) || 10657 TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) == -1 || /* unknown number of args */ 10658 (uint32_t)TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) > op_array->num_args; /* extra args */ 10659 bool may_need_release_this = 10660 !(op_array->fn_flags & ZEND_ACC_CLOSURE) && 10661 op_array->scope && 10662 !(op_array->fn_flags & ZEND_ACC_STATIC) && 10663 (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 10664 !JIT_G(current_frame) || 10665 !TRACE_FRAME_NO_NEED_RELEASE_THIS(JIT_G(current_frame))); 10666 10667 if (may_need_call_helper || may_need_release_this) { 10668 | ldr FCARG1w, [FP, #offsetof(zend_execute_data, This.u1.type_info)] 10669 } 10670 if (may_need_call_helper) { 10671 if (!left_frame) { 10672 left_frame = 1; 10673 if (!zend_jit_leave_frame(Dst)) { 10674 return 0; 10675 } 10676 } 10677 /* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */ 10678 10679 | 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 10680 if (trace && trace->op != ZEND_JIT_TRACE_END) { 10681 | bne >1 10682 |.cold_code 10683 |1: 10684 if (!GCC_GLOBAL_REGS) { 10685 | mov FCARG1x, FP 10686 } 10687 | EXT_CALL zend_jit_leave_func_helper, REG0 10688 10689 if (may_be_top_frame) { 10690 // TODO: try to avoid this check ??? 10691 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 10692#if 0 10693 /* this check should be handled by the following OPLINE guard */ 10694 | LOAD_ADDR TMP1, zend_jit_halt_op 10695 | cmp IP, TMP1 10696 | beq ->trace_halt 10697#endif 10698 } else if (GCC_GLOBAL_REGS) { 10699 | cbz IP, ->trace_halt 10700 } else { 10701 | tst RETVALw, RETVALw 10702 | blt ->trace_halt 10703 } 10704 } 10705 10706 if (!GCC_GLOBAL_REGS) { 10707 | // execute_data = EG(current_execute_data) 10708 | MEM_LOAD_64_ZTS ldr, FP, executor_globals, current_execute_data, TMP1 10709 } 10710 | b >8 10711 |.code 10712 } else { 10713 | bne ->leave_function_handler 10714 } 10715 } 10716 10717 if (op_array->fn_flags & ZEND_ACC_CLOSURE) { 10718 if (!left_frame) { 10719 left_frame = 1; 10720 if (!zend_jit_leave_frame(Dst)) { 10721 return 0; 10722 } 10723 } 10724 | // OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); 10725 | ldr FCARG1x, EX->func 10726 | sub FCARG1x, FCARG1x, #sizeof(zend_object) 10727 | OBJ_RELEASE ZREG_FCARG1, >4, ZREG_TMP1, ZREG_TMP2 10728 |4: 10729 } else if (may_need_release_this) { 10730 if (!left_frame) { 10731 left_frame = 1; 10732 if (!zend_jit_leave_frame(Dst)) { 10733 return 0; 10734 } 10735 } 10736 if (!JIT_G(current_frame) || !TRACE_FRAME_ALWAYS_RELEASE_THIS(JIT_G(current_frame))) { 10737 | // if (call_info & ZEND_CALL_RELEASE_THIS) 10738 | TST_32_WITH_CONST FCARG1w, ZEND_CALL_RELEASE_THIS, TMP1w 10739 | beq >4 10740 } 10741 | // zend_object *object = Z_OBJ(execute_data->This); 10742 | ldr FCARG1x, EX->This.value.obj 10743 | // OBJ_RELEASE(object); 10744 | OBJ_RELEASE ZREG_FCARG1, >4, ZREG_TMP1, ZREG_TMP2 10745 |4: 10746 // TODO: avoid EG(excption) check for $this->foo() calls 10747 may_throw = 1; 10748 } 10749 10750 | // EG(vm_stack_top) = (zval*)execute_data; 10751 | MEM_STORE_64_ZTS str, FP, executor_globals, vm_stack_top, REG0 10752 | // execute_data = EX(prev_execute_data); 10753 | ldr FP, EX->prev_execute_data 10754 10755 if (!left_frame) { 10756 | // EG(current_execute_data) = execute_data; 10757 | MEM_STORE_64_ZTS str, FP, executor_globals, current_execute_data, REG0 10758 } 10759 10760 |9: 10761 if (trace) { 10762 if (trace->op != ZEND_JIT_TRACE_END 10763 && (JIT_G(current_frame) && !TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) { 10764 zend_jit_reset_last_valid_opline(); 10765 } else { 10766 | LOAD_IP 10767 | ADD_IP_WITH_CONST sizeof(zend_op), TMP1 10768 } 10769 10770 |8: 10771 10772 if (trace->op == ZEND_JIT_TRACE_BACK 10773 && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) { 10774 const zend_op *next_opline = trace->opline; 10775 10776 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) 10777 && (op1_info & MAY_BE_RC1) 10778 && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { 10779 /* exception might be thrown during destruction of unused return value */ 10780 | // if (EG(exception)) 10781 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 10782 | cbnz REG0, ->leave_throw_handler 10783 } 10784 do { 10785 trace++; 10786 } while (trace->op == ZEND_JIT_TRACE_INIT_CALL); 10787 ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END); 10788 next_opline = trace->opline; 10789 ZEND_ASSERT(next_opline != NULL); 10790 10791 if (trace->op == ZEND_JIT_TRACE_END 10792 && trace->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) { 10793 trace_info->flags |= ZEND_JIT_TRACE_LOOP; 10794 | CMP_IP next_opline, TMP1, TMP2 10795 | beq =>0 // LOOP 10796#ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE 10797 | JMP_IP TMP1 10798#else 10799 | b ->trace_escape 10800#endif 10801 } else { 10802 | CMP_IP next_opline, TMP1, TMP2 10803 | bne ->trace_escape 10804 } 10805 10806 zend_jit_set_last_valid_opline(trace->opline); 10807 10808 return 1; 10809 } else if (may_throw || 10810 (((opline->op1_type & (IS_VAR|IS_TMP_VAR)) 10811 && (op1_info & MAY_BE_RC1) 10812 && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) 10813 && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) { 10814 | // if (EG(exception)) 10815 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 10816 | cbnz REG0, ->leave_throw_handler 10817 } 10818 10819 return 1; 10820 } else { 10821 | // if (EG(exception)) 10822 | MEM_LOAD_64_ZTS ldr, REG0, executor_globals, exception, TMP1 10823 | LOAD_IP 10824 | cbnz REG0, ->leave_throw_handler 10825 | // opline = EX(opline) + 1 10826 | ADD_IP_WITH_CONST sizeof(zend_op), TMP1 10827 } 10828 10829 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { 10830 | ADD_HYBRID_SPAD 10831#ifdef CONTEXT_THREADED_JIT 10832 | NIY // TODO: CONTEXT_THREADED_JIT is always undefined 10833#else 10834 | JMP_IP TMP1 10835#endif 10836 } else if (GCC_GLOBAL_REGS) { 10837 | ldp x29, x30, [sp], # SPAD // stack alignment 10838#ifdef CONTEXT_THREADED_JIT 10839 | NIY // TODO 10840#else 10841 | JMP_IP TMP1 10842#endif 10843 } else { 10844#ifdef CONTEXT_THREADED_JIT 10845 ZEND_UNREACHABLE(); 10846 // TODO: context threading can't work without GLOBAL REGS because we have to change 10847 // the value of execute_data in execute_ex() 10848 | NIY // TODO 10849#else 10850 | ldp FP, RX, T2 // restore FP and IP 10851 | ldp x29, x30, [sp], # NR_SPAD // stack alignment 10852 | mov RETVALx, #2 // ZEND_VM_LEAVE ???? 10853 | ret 10854#endif 10855 } 10856 10857 return 1; 10858} 10859 10860static 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) 10861{ 10862 zend_jit_addr ret_addr; 10863 int8_t return_value_used; 10864 10865 ZEND_ASSERT(op_array->type != ZEND_EVAL_CODE && op_array->function_name); 10866 ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF)); 10867 10868 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && JIT_G(current_frame)) { 10869 if (TRACE_FRAME_IS_RETURN_VALUE_USED(JIT_G(current_frame))) { 10870 return_value_used = 1; 10871 } else if (TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))) { 10872 return_value_used = 0; 10873 } else { 10874 return_value_used = -1; 10875 } 10876 } else { 10877 return_value_used = -1; 10878 } 10879 10880 if (ZEND_OBSERVER_ENABLED) { 10881 if (Z_MODE(op1_addr) == IS_REG) { 10882 zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); 10883 10884 if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) { 10885 return 0; 10886 } 10887 op1_addr = dst; 10888 } 10889 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 10890 | mov FCARG1x, FP 10891 | SET_EX_OPLINE opline, REG0 10892 | EXT_CALL zend_observer_fcall_end, REG0 10893 } 10894 10895 // if (!EX(return_value)) 10896 if (Z_MODE(op1_addr) == IS_REG && Z_REG(op1_addr) == ZREG_REG1) { 10897 if (return_value_used != 0) { 10898 | ldr REG2, EX->return_value 10899 } 10900 ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG2, 0); 10901 } else { 10902 if (return_value_used != 0) { 10903 | ldr REG1, EX->return_value 10904 } 10905 ret_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG1, 0); 10906 } 10907 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && 10908 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10909 if (return_value_used == -1) { 10910 | cbz Rx(Z_REG(ret_addr)), >1 10911 |.cold_code 10912 |1: 10913 } 10914 if (return_value_used != 1) { 10915 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 10916 if (jit_return_label >= 0) { 10917 | IF_NOT_ZVAL_REFCOUNTED op1_addr, =>jit_return_label, ZREG_TMP1, ZREG_TMP2 10918 } else { 10919 | IF_NOT_ZVAL_REFCOUNTED op1_addr, >9, ZREG_TMP1, ZREG_TMP2 10920 } 10921 } 10922 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 10923 | GC_DELREF FCARG1x, TMP1w 10924 if (RC_MAY_BE_1(op1_info)) { 10925 if (RC_MAY_BE_N(op1_info)) { 10926 if (jit_return_label >= 0) { 10927 | bne =>jit_return_label 10928 } else { 10929 | bne >9 10930 } 10931 } 10932 | //SAVE_OPLINE() 10933 | ZVAL_DTOR_FUNC op1_info, opline, TMP1 10934 | //????ldr REG1, EX->return_value // reload ??? 10935 } 10936 if (return_value_used == -1) { 10937 if (jit_return_label >= 0) { 10938 | b =>jit_return_label 10939 } else { 10940 | b >9 10941 } 10942 |.code 10943 } 10944 } 10945 } else if (return_value_used == -1) { 10946 if (jit_return_label >= 0) { 10947 | cbz Rx(Z_REG(ret_addr)), =>jit_return_label 10948 } else { 10949 | cbz Rx(Z_REG(ret_addr)), >9 10950 } 10951 } 10952 10953 if (return_value_used == 0) { 10954 |9: 10955 return 1; 10956 } 10957 10958 if (opline->op1_type == IS_CONST) { 10959 zval *zv = RT_CONSTANT(opline, opline->op1); 10960 | ZVAL_COPY_CONST ret_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0 10961 if (Z_REFCOUNTED_P(zv)) { 10962 | ADDREF_CONST zv, REG0, TMP1 10963 } 10964 } else if (opline->op1_type == IS_TMP_VAR) { 10965 | ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10966 } else if (opline->op1_type == IS_CV) { 10967 if (op1_info & MAY_BE_REF) { 10968 | LOAD_ZVAL_ADDR REG0, op1_addr 10969 | ZVAL_DEREF REG0, op1_info, TMP1w 10970 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 10971 } 10972 | ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10973 if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 10974 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 10975 (op1_info & (MAY_BE_REF|MAY_BE_OBJECT)) || 10976 !op_array->function_name) { 10977 | TRY_ADDREF op1_info, REG0w, REG2, TMP1w 10978 } else if (return_value_used != 1) { 10979 | // if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) ZVAL_NULL(retval_ptr); 10980 | SET_ZVAL_TYPE_INFO op1_addr, IS_NULL, TMP1w, TMP2 10981 } 10982 } 10983 } else { 10984 if (op1_info & MAY_BE_REF) { 10985 zend_jit_addr ref_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, offsetof(zend_reference, val)); 10986 10987 | IF_ZVAL_TYPE op1_addr, IS_REFERENCE, >1, ZREG_TMP1 10988 |.cold_code 10989 |1: 10990 | // zend_refcounted *ref = Z_COUNTED_P(retval_ptr); 10991 | GET_ZVAL_PTR REG0, op1_addr, TMP1 10992 | // ZVAL_COPY_VALUE(return_value, &ref->value); 10993 | ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, ref_addr, op1_info, ZREG_REG2, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 10994 | GC_DELREF REG0, TMP1w 10995 | beq >2 10996 | // if (IS_REFCOUNTED()) 10997 if (jit_return_label >= 0) { 10998 | IF_NOT_REFCOUNTED REG2w, =>jit_return_label, TMP1w 10999 } else { 11000 | IF_NOT_REFCOUNTED REG2w, >9, TMP1w 11001 } 11002 | // ADDREF 11003 | GET_ZVAL_PTR REG2, ret_addr, TMP1 // reload 11004 | GC_ADDREF REG2, TMP1w 11005 if (jit_return_label >= 0) { 11006 | b =>jit_return_label 11007 } else { 11008 | b >9 11009 } 11010 |2: 11011 | mov FCARG1x, REG0 11012 | EFREE_REFERENCE 11013 if (jit_return_label >= 0) { 11014 | b =>jit_return_label 11015 } else { 11016 | b >9 11017 } 11018 |.code 11019 } 11020 | ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 11021 } 11022 11023 |9: 11024 return 1; 11025} 11026 11027static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, zend_jit_addr val_addr, zend_reg type_reg) 11028{ 11029 ZEND_ASSERT(type_reg == ZREG_REG2); 11030 11031 | GET_ZVAL_PTR REG1, val_addr, TMP1 11032 | IF_NOT_REFCOUNTED REG2w, >2, TMP1w 11033 | GET_LOW_8BITS TMP2w, REG2w 11034 | IF_NOT_TYPE TMP2w, IS_REFERENCE, >1 11035 | add REG1, REG1, #offsetof(zend_reference, val) 11036 | GET_Z_TYPE_INFO REG2w, REG1 11037 | GET_Z_PTR REG1, REG1 11038 | IF_NOT_REFCOUNTED REG2w, >2, TMP1w 11039 |1: 11040 | GC_ADDREF REG1, TMP2w 11041 |2: 11042 | SET_ZVAL_PTR res_addr, REG1, TMP1 11043 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1 11044 11045 return 1; 11046} 11047 11048static int zend_jit_fetch_dim_read(dasm_State **Dst, 11049 const zend_op *opline, 11050 zend_ssa *ssa, 11051 const zend_ssa_op *ssa_op, 11052 uint32_t op1_info, 11053 zend_jit_addr op1_addr, 11054 bool op1_avoid_refcounting, 11055 uint32_t op2_info, 11056 uint32_t res_info, 11057 zend_jit_addr res_addr, 11058 uint8_t dim_type) 11059{ 11060 zend_jit_addr orig_op1_addr, op2_addr; 11061 const void *exit_addr = NULL; 11062 const void *not_found_exit_addr = NULL; 11063 const void *res_exit_addr = NULL; 11064 bool result_avoid_refcounting = 0; 11065 uint32_t may_be_string = (opline->opcode != ZEND_FETCH_LIST_R) ? MAY_BE_STRING : 0; 11066 int may_throw = 0; 11067 11068 orig_op1_addr = OP1_ADDR(); 11069 op2_addr = OP2_ADDR(); 11070 11071 if (opline->opcode != ZEND_FETCH_DIM_IS 11072 && JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 11073 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 11074 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 11075 if (!exit_addr) { 11076 return 0; 11077 } 11078 } 11079 11080 if ((res_info & MAY_BE_GUARD) 11081 && JIT_G(current_frame) 11082 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { 11083 uint32_t flags = 0; 11084 uint32_t old_op1_info = 0; 11085 uint32_t old_info; 11086 zend_jit_trace_stack *stack = JIT_G(current_frame)->stack; 11087 int32_t exit_point; 11088 11089 if (opline->opcode != ZEND_FETCH_LIST_R 11090 && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) 11091 && !op1_avoid_refcounting) { 11092 flags |= ZEND_JIT_EXIT_FREE_OP1; 11093 } 11094 if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) 11095 && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 11096 flags |= ZEND_JIT_EXIT_FREE_OP2; 11097 } 11098 if ((opline->result_type & (IS_VAR|IS_TMP_VAR)) 11099 && !(flags & ZEND_JIT_EXIT_FREE_OP1) 11100 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) 11101 && (ssa_op+1)->op1_use == ssa_op->result_def 11102 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG))) 11103 && zend_jit_may_avoid_refcounting(opline+1, res_info)) { 11104 result_avoid_refcounting = 1; 11105 ssa->var_info[ssa_op->result_def].avoid_refcounting = 1; 11106 } 11107 11108 if (op1_avoid_refcounting) { 11109 old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var)); 11110 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE); 11111 } 11112 11113 if (!(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG)))) { 11114 old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); 11115 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1); 11116 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0); 11117 exit_point = zend_jit_trace_get_exit_point(opline+1, flags); 11118 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info); 11119 res_exit_addr = zend_jit_trace_get_exit_addr(exit_point); 11120 if (!res_exit_addr) { 11121 return 0; 11122 } 11123 res_info &= ~MAY_BE_GUARD; 11124 ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; 11125 } 11126 11127 if (opline->opcode == ZEND_FETCH_DIM_IS 11128 && !(res_info & MAY_BE_NULL)) { 11129 old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); 11130 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_NULL, 0); 11131 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_NULL); 11132 exit_point = zend_jit_trace_get_exit_point(opline+1, flags); 11133 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info); 11134 not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point); 11135 if (!not_found_exit_addr) { 11136 return 0; 11137 } 11138 } 11139 11140 if (op1_avoid_refcounting) { 11141 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info); 11142 } 11143 } 11144 11145 if (op1_info & MAY_BE_REF) { 11146 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 11147 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 11148 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 11149 } 11150 11151 if (op1_info & MAY_BE_ARRAY) { 11152 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) { 11153 if (exit_addr && !(op1_info & (MAY_BE_OBJECT|may_be_string))) { 11154 | IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, &exit_addr, ZREG_TMP1 11155 } else { 11156 | IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1 11157 } 11158 } 11159 | GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1 11160 if ((op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) || 11161 (opline->opcode != ZEND_FETCH_DIM_IS && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE)) { 11162 may_throw = 1; 11163 } 11164 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)) { 11165 return 0; 11166 } 11167 } 11168 11169 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) { 11170 if (op1_info & MAY_BE_ARRAY) { 11171 |.cold_code 11172 |7: 11173 } 11174 11175 if (opline->opcode != ZEND_FETCH_LIST_R && (op1_info & MAY_BE_STRING)) { 11176 may_throw = 1; 11177 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING))) { 11178 if (exit_addr && !(op1_info & MAY_BE_OBJECT)) { 11179 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &exit_addr, ZREG_TMP1 11180 } else { 11181 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1 11182 } 11183 } 11184 | SET_EX_OPLINE opline, REG0 11185 | GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1 11186 if (opline->opcode != ZEND_FETCH_DIM_IS) { 11187 if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) { 11188 | GET_ZVAL_LVAL ZREG_FCARG2, op2_addr, TMP1 11189 | EXT_CALL zend_jit_fetch_dim_str_offset_r_helper, REG0 11190 } else { 11191 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 11192 | EXT_CALL zend_jit_fetch_dim_str_r_helper, REG0 11193 } 11194 | SET_ZVAL_PTR res_addr, RETVALx, TMP1 11195 | SET_ZVAL_TYPE_INFO res_addr, IS_STRING, TMP1w, TMP2 11196 } else { 11197 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 11198 | LOAD_ZVAL_ADDR CARG3, res_addr 11199 | EXT_CALL zend_jit_fetch_dim_str_is_helper, REG0 11200 } 11201 if ((op1_info & MAY_BE_ARRAY) || 11202 (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_STRING)))) { 11203 | b >9 // END 11204 } 11205 |6: 11206 } 11207 11208 if (op1_info & MAY_BE_OBJECT) { 11209 may_throw = 1; 11210 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string))) { 11211 if (exit_addr) { 11212 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1 11213 } else { 11214 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >6, ZREG_TMP1 11215 } 11216 } 11217 | SET_EX_OPLINE opline, REG0 11218 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 11219 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 11220 } 11221 if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) { 11222 ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL); 11223 | LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1) 11224 } else { 11225 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 11226 } 11227 | LOAD_ZVAL_ADDR CARG3, res_addr 11228 if (opline->opcode != ZEND_FETCH_DIM_IS) { 11229 | EXT_CALL zend_jit_fetch_dim_obj_r_helper, REG0 11230 } else { 11231 | EXT_CALL zend_jit_fetch_dim_obj_is_helper, REG0 11232 } 11233 if ((op1_info & MAY_BE_ARRAY) || 11234 (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) { 11235 | b >9 // END 11236 } 11237 |6: 11238 } 11239 11240 if ((op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string))) 11241 && (!exit_addr || !(op1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|may_be_string)))) { 11242 if ((opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) || (op2_info & MAY_BE_UNDEF)) { 11243 | SET_EX_OPLINE opline, REG0 11244 if (opline->opcode != ZEND_FETCH_DIM_IS && (op1_info & MAY_BE_UNDEF)) { 11245 may_throw = 1; 11246 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 11247 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 11248 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 11249 | EXT_CALL zend_jit_undefined_op_helper, REG0 11250 |1: 11251 } 11252 11253 if (op2_info & MAY_BE_UNDEF) { 11254 may_throw = 1; 11255 | IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1 11256 | LOAD_32BIT_VAL FCARG1w, opline->op2.var 11257 | EXT_CALL zend_jit_undefined_op_helper, REG0 11258 |1: 11259 } 11260 } 11261 11262 if (opline->opcode != ZEND_FETCH_DIM_IS && opline->opcode != ZEND_FETCH_LIST_R) { 11263 may_throw = 1; 11264 if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) { 11265 | LOAD_ZVAL_ADDR FCARG1x, orig_op1_addr 11266 } else { 11267 | SET_EX_OPLINE opline, REG0 11268 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || 11269 Z_REG(op1_addr) != ZREG_FCARG1 || 11270 Z_OFFSET(op1_addr) != 0) { 11271 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 11272 } 11273 } 11274 | EXT_CALL zend_jit_invalid_array_access, REG0 11275 } 11276 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 11277 if (op1_info & MAY_BE_ARRAY) { 11278 | b >9 // END 11279 } 11280 } 11281 11282 if (op1_info & MAY_BE_ARRAY) { 11283 |.code 11284 } 11285 } 11286 11287 if (op1_info & MAY_BE_ARRAY) { 11288 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 11289 11290 |8: 11291 if (res_exit_addr) { 11292 uint32_t type = concrete_type(res_info); 11293 if ((op1_info & MAY_BE_ARRAY_OF_REF) 11294 && dim_type != IS_UNKNOWN 11295 && dim_type != IS_REFERENCE) { 11296 if (type < IS_STRING) { 11297 | IF_NOT_ZVAL_TYPE val_addr, type, >1, ZREG_TMP1 11298 |.cold_code 11299 |1: 11300 | IF_NOT_ZVAL_TYPE val_addr, IS_REFERENCE, &res_exit_addr, ZREG_TMP1 11301 | GET_Z_PTR REG0, REG0 11302 | add REG0, REG0, #offsetof(zend_reference, val) 11303 | IF_ZVAL_TYPE val_addr, type, >1, ZREG_TMP1 11304 | b &res_exit_addr 11305 |.code 11306 |1: 11307 } else { 11308 | GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1 11309 | GET_LOW_8BITS TMP1w, REG2w 11310 | IF_NOT_TYPE TMP1w, type, >1 11311 |.cold_code 11312 |1: 11313 | IF_NOT_TYPE TMP1w, IS_REFERENCE, &res_exit_addr 11314 | GET_Z_PTR REG0, REG0 11315 | add REG0, REG0, #offsetof(zend_reference, val) 11316 | GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1 11317 | GET_LOW_8BITS TMP1w, REG2w 11318 | IF_TYPE TMP1w, type, >1 11319 | b &res_exit_addr 11320 |.code 11321 |1: 11322 } 11323 } else { 11324 if (op1_info & MAY_BE_ARRAY_OF_REF) { 11325 | ZVAL_DEREF REG0, MAY_BE_REF, TMP1w 11326 } 11327 if (type < IS_STRING) { 11328 | IF_NOT_ZVAL_TYPE val_addr, type, &res_exit_addr, ZREG_TMP1 11329 } else { 11330 | GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1 11331 | GET_LOW_8BITS TMP1w, REG2w 11332 | IF_NOT_TYPE TMP1w, type, &res_exit_addr 11333 } 11334 } 11335 | // ZVAL_COPY 11336 |7: 11337 | ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0 11338 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 11339 if (type < IS_STRING) { 11340 if (Z_REG(res_addr) != ZREG_FP || 11341 JIT_G(current_frame) == NULL || 11342 STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) { 11343 | SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2 11344 } 11345 } else { 11346 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1 11347 if (!result_avoid_refcounting) { 11348 | TRY_ADDREF res_info, REG2w, REG1, TMP1w 11349 } 11350 } 11351 } else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) { 11352 return 0; 11353 } 11354 } else if (op1_info & MAY_BE_ARRAY_OF_REF) { 11355 | // ZVAL_COPY_DEREF 11356 | GET_ZVAL_TYPE_INFO Rw(ZREG_REG2), val_addr, TMP1 11357 if (!zend_jit_zval_copy_deref(Dst, res_addr, val_addr, ZREG_REG2)) { 11358 return 0; 11359 } 11360 } else { 11361 | // ZVAL_COPY 11362 | ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 11363 | TRY_ADDREF res_info, REG1w, REG2, TMP1w 11364 } 11365 } 11366 |9: // END 11367 11368#ifdef ZEND_JIT_USE_RC_INFERENCE 11369 if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) { 11370 /* Magic offsetGet() may increase refcount of the key */ 11371 op2_info |= MAY_BE_RCN; 11372 } 11373#endif 11374 11375 if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) { 11376 if ((op2_info & MAY_HAVE_DTOR) && (op2_info & MAY_BE_RC1)) { 11377 may_throw = 1; 11378 } 11379 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 11380 } 11381 if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting) { 11382 if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { 11383 if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) { 11384 may_throw = 1; 11385 } 11386 | FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2 11387 } 11388 } 11389 11390 if (may_throw) { 11391 if (!zend_jit_check_exception(Dst)) { 11392 return 0; 11393 } 11394 } 11395 11396 return 1; 11397} 11398 11399static int zend_jit_fetch_dim(dasm_State **Dst, 11400 const zend_op *opline, 11401 uint32_t op1_info, 11402 zend_jit_addr op1_addr, 11403 uint32_t op2_info, 11404 zend_jit_addr res_addr, 11405 uint8_t dim_type) 11406{ 11407 zend_jit_addr op2_addr; 11408 int may_throw = 0; 11409 11410 op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0; 11411 11412 if (opline->opcode == ZEND_FETCH_DIM_RW) { 11413 | SET_EX_OPLINE opline, REG0 11414 } 11415 if (op1_info & MAY_BE_REF) { 11416 may_throw = 1; 11417 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 11418 | IF_NOT_Z_TYPE FCARG1x, IS_REFERENCE, >1, TMP1w 11419 | GET_Z_PTR FCARG2x, FCARG1x 11420 | ldrb TMP1w, [FCARG2x, #(offsetof(zend_reference, val) + offsetof(zval, u1.v.type))] 11421 | cmp TMP1w, #IS_ARRAY 11422 | bne >2 11423 | add FCARG1x, FCARG2x, #offsetof(zend_reference, val) 11424 | b >3 11425 |.cold_code 11426 |2: 11427 | SET_EX_OPLINE opline, REG0 11428 if (opline->opcode != ZEND_FETCH_DIM_RW) { 11429 | EXT_CALL zend_jit_prepare_assign_dim_ref, REG0 11430 } 11431 | mov FCARG1x, RETVALx 11432 | cbnz FCARG1x, >1 11433 | b ->exception_handler_undef 11434 |.code 11435 |1: 11436 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 11437 } 11438 11439 if (op1_info & MAY_BE_ARRAY) { 11440 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) { 11441 | IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1 11442 } 11443 |3: 11444 | SEPARATE_ARRAY op1_addr, op1_info, 1, ZREG_TMP1, ZREG_TMP2 11445 } 11446 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL)) { 11447 if (op1_info & MAY_BE_ARRAY) { 11448 |.cold_code 11449 |7: 11450 } 11451 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 11452 | CMP_ZVAL_TYPE op1_addr, IS_NULL, ZREG_TMP1 11453 | bgt >7 11454 } 11455 if (Z_REG(op1_addr) != ZREG_FP) { 11456 | str Rx(Z_REG(op1_addr)), T1 // save 11457 } 11458 if ((op1_info & MAY_BE_UNDEF) 11459 && opline->opcode == ZEND_FETCH_DIM_RW) { 11460 may_throw = 1; 11461 if (op1_info & MAY_BE_NULL) { 11462 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 11463 } 11464 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 11465 | EXT_CALL zend_jit_undefined_op_helper, REG0 11466 |1: 11467 } 11468 | // ZVAL_ARR(container, zend_new_array(8)); 11469 | EXT_CALL _zend_new_array_0, REG0 11470 | mov REG0, RETVALx 11471 if (Z_REG(op1_addr) != ZREG_FP) { 11472 | ldr Rx(Z_REG(op1_addr)), T1 // restore 11473 } 11474 | SET_ZVAL_LVAL_FROM_REG op1_addr, REG0, TMP1 11475 | SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX, TMP1w, TMP2 11476 | mov FCARG1x, REG0 11477 if (op1_info & MAY_BE_ARRAY) { 11478 | b >1 11479 |.code 11480 |1: 11481 } 11482 } 11483 11484 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 11485 |6: 11486 if (opline->op2_type == IS_UNUSED) { 11487 may_throw = 1; 11488 | // var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); 11489 | LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval 11490 | EXT_CALL zend_hash_next_index_insert, REG0 11491 | // if (UNEXPECTED(!var_ptr)) { 11492 | cbz RETVALx, >1 11493 |.cold_code 11494 |1: 11495 | // zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied"); 11496 | CANNOT_ADD_ELEMENT opline 11497 | SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF, TMP1w, TMP2 11498 | //ZEND_VM_C_GOTO(assign_dim_op_ret_null); 11499 | b >8 11500 |.code 11501 | SET_ZVAL_PTR res_addr, RETVALx, TMP1 11502 | SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2 11503 } else { 11504 uint32_t type; 11505 11506 switch (opline->opcode) { 11507 case ZEND_FETCH_DIM_W: 11508 case ZEND_FETCH_LIST_W: 11509 type = BP_VAR_W; 11510 break; 11511 case ZEND_FETCH_DIM_RW: 11512 may_throw = 1; 11513 type = BP_VAR_RW; 11514 break; 11515 case ZEND_FETCH_DIM_UNSET: 11516 type = BP_VAR_UNSET; 11517 break; 11518 default: 11519 ZEND_UNREACHABLE(); 11520 } 11521 11522 if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING))) { 11523 may_throw = 1; 11524 } 11525 if (!zend_jit_fetch_dimension_address_inner(Dst, opline, type, op1_info, op2_info, dim_type, NULL, NULL, NULL)) { 11526 return 0; 11527 } 11528 11529 |8: 11530 | SET_ZVAL_PTR res_addr, REG0, TMP1 11531 | SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2 11532 11533 if (type == BP_VAR_RW || (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) { 11534 |.cold_code 11535 |9: 11536 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 11537 | b >8 11538 |.code 11539 } 11540 } 11541 } 11542 11543 if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_ARRAY))) { 11544 may_throw = 1; 11545 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 11546 |.cold_code 11547 |7: 11548 } 11549 11550 if (opline->opcode != ZEND_FETCH_DIM_RW) { 11551 | SET_EX_OPLINE opline, REG0 11552 } 11553 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 11554 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 11555 } 11556 if (opline->op2_type == IS_UNUSED) { 11557 | mov FCARG2x, xzr 11558 } else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) { 11559 ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL); 11560 | LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1) 11561 } else { 11562 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 11563 } 11564 | LOAD_ZVAL_ADDR CARG3, res_addr 11565 switch (opline->opcode) { 11566 case ZEND_FETCH_DIM_W: 11567 case ZEND_FETCH_LIST_W: 11568 | EXT_CALL zend_jit_fetch_dim_obj_w_helper, REG0 11569 break; 11570 case ZEND_FETCH_DIM_RW: 11571 | EXT_CALL zend_jit_fetch_dim_obj_rw_helper, REG0 11572 break; 11573// case ZEND_FETCH_DIM_UNSET: 11574// | EXT_CALL zend_jit_fetch_dim_obj_unset_helper, REG0 11575// break; 11576 default: 11577 ZEND_UNREACHABLE(); 11578 } 11579 11580 if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_ARRAY)) { 11581 | b >8 // END 11582 |.code 11583 } 11584 } 11585 11586#ifdef ZEND_JIT_USE_RC_INFERENCE 11587 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))) { 11588 /* ASSIGN_DIM may increase refcount of the key */ 11589 op2_info |= MAY_BE_RCN; 11590 } 11591#endif 11592 11593 if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) 11594 && (op2_info & MAY_HAVE_DTOR) 11595 && (op2_info & MAY_BE_RC1)) { 11596 may_throw = 1; 11597 } 11598 |8: 11599 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 11600 11601 if (may_throw) { 11602 if (!zend_jit_check_exception(Dst)) { 11603 return 0; 11604 } 11605 } 11606 return 1; 11607} 11608 11609static int zend_jit_isset_isempty_dim(dasm_State **Dst, 11610 const zend_op *opline, 11611 uint32_t op1_info, 11612 zend_jit_addr op1_addr, 11613 bool op1_avoid_refcounting, 11614 uint32_t op2_info, 11615 uint8_t dim_type, 11616 int may_throw, 11617 zend_uchar smart_branch_opcode, 11618 uint32_t target_label, 11619 uint32_t target_label2, 11620 const void *exit_addr) 11621{ 11622 zend_jit_addr op2_addr, res_addr; 11623 11624 // TODO: support for empty() ??? 11625 ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY)); 11626 11627 op2_addr = OP2_ADDR(); 11628 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 11629 11630 if (op1_info & MAY_BE_REF) { 11631 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 11632 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 11633 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 11634 } 11635 11636 if (op1_info & MAY_BE_ARRAY) { 11637 const void *found_exit_addr = NULL; 11638 const void *not_found_exit_addr = NULL; 11639 11640 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) { 11641 | IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1 11642 } 11643 | GET_ZVAL_LVAL ZREG_FCARG1, op1_addr, TMP1 11644 if (exit_addr 11645 && !(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) 11646 && !may_throw 11647 && (!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || op1_avoid_refcounting) 11648 && (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)))) { 11649 if (smart_branch_opcode == ZEND_JMPNZ) { 11650 found_exit_addr = exit_addr; 11651 } else { 11652 not_found_exit_addr = exit_addr; 11653 } 11654 } 11655 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)) { 11656 return 0; 11657 } 11658 11659 if (found_exit_addr) { 11660 |9: 11661 return 1; 11662 } else if (not_found_exit_addr) { 11663 |8: 11664 return 1; 11665 } 11666 } 11667 11668 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_ARRAY)) { 11669 if (op1_info & MAY_BE_ARRAY) { 11670 |.cold_code 11671 |7: 11672 } 11673 11674 if (op1_info & (MAY_BE_STRING|MAY_BE_OBJECT)) { 11675 | SET_EX_OPLINE opline, REG0 11676 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 11677 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 11678 } 11679 if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) { 11680 ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL); 11681 | LOAD_ADDR FCARG2x, (Z_ZV(op2_addr) + 1) 11682 } else { 11683 | LOAD_ZVAL_ADDR FCARG2x, op2_addr 11684 } 11685 | EXT_CALL zend_jit_isset_dim_helper, REG0 11686 | cbz RETVALw, >9 11687 if (op1_info & MAY_BE_ARRAY) { 11688 | b >8 11689 |.code 11690 } 11691 } else { 11692 if (op2_info & MAY_BE_UNDEF) { 11693 if (op2_info & MAY_BE_ANY) { 11694 | IF_NOT_ZVAL_TYPE op2_addr, IS_UNDEF, >1, ZREG_TMP1 11695 } 11696 | SET_EX_OPLINE opline, REG0 11697 | LOAD_32BIT_VAL FCARG1w, opline->op2.var 11698 | EXT_CALL zend_jit_undefined_op_helper, REG0 11699 |1: 11700 } 11701 if (op1_info & MAY_BE_ARRAY) { 11702 | b >9 11703 |.code 11704 } 11705 } 11706 } 11707 11708#ifdef ZEND_JIT_USE_RC_INFERENCE 11709 if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op1_info & MAY_BE_OBJECT)) { 11710 /* Magic offsetExists() may increase refcount of the key */ 11711 op2_info |= MAY_BE_RCN; 11712 } 11713#endif 11714 11715 if (op1_info & (MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_OBJECT)) { 11716 |8: 11717 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 11718 if (!op1_avoid_refcounting) { 11719 | FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2 11720 } 11721 if (may_throw) { 11722 if (!zend_jit_check_exception_undef_result(Dst, opline)) { 11723 return 0; 11724 } 11725 } 11726 if (!(opline->extended_value & ZEND_ISEMPTY)) { 11727 if (exit_addr) { 11728 if (smart_branch_opcode == ZEND_JMPNZ) { 11729 | b &exit_addr 11730 } else { 11731 | b >8 11732 } 11733 } else if (smart_branch_opcode) { 11734 if (smart_branch_opcode == ZEND_JMPZ) { 11735 | b =>target_label2 11736 } else if (smart_branch_opcode == ZEND_JMPNZ) { 11737 | b =>target_label 11738 } else { 11739 ZEND_UNREACHABLE(); 11740 } 11741 } else { 11742 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 11743 | b >8 11744 } 11745 } else { 11746 | NIY // TODO: support for empty() 11747 } 11748 } 11749 11750 |9: // not found 11751 | FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 11752 if (!op1_avoid_refcounting) { 11753 | FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2 11754 } 11755 if (may_throw) { 11756 if (!zend_jit_check_exception_undef_result(Dst, opline)) { 11757 return 0; 11758 } 11759 } 11760 if (!(opline->extended_value & ZEND_ISEMPTY)) { 11761 if (exit_addr) { 11762 if (smart_branch_opcode == ZEND_JMPZ) { 11763 | b &exit_addr 11764 } 11765 } else if (smart_branch_opcode) { 11766 if (smart_branch_opcode == ZEND_JMPZ) { 11767 | b =>target_label 11768 } else if (smart_branch_opcode == ZEND_JMPNZ) { 11769 } else { 11770 ZEND_UNREACHABLE(); 11771 } 11772 } else { 11773 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 11774 } 11775 } else { 11776 | NIY // TODO: support for empty() 11777 } 11778 11779 |8: 11780 11781 return 1; 11782} 11783 11784static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_t op1_info) 11785{ 11786 zend_jit_addr op1_addr = OP1_ADDR(); 11787 zend_string *varname = Z_STR_P(RT_CONSTANT(opline, opline->op2)); 11788 11789 | // idx = (uint32_t)(uintptr_t)CACHED_PTR(opline->extended_value) - 1; 11790 | ldr FCARG2x, EX->run_time_cache 11791 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, FCARG2x, opline->extended_value, TMP1 11792 | sub REG0, REG0, #1 11793 | // if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket))) 11794 | MEM_LOAD_32_ZTS ldr, REG1w, executor_globals, symbol_table.nNumUsed, REG1 11795 | cmp REG0, REG1, lsl #5 11796 | bhs >9 11797 | // Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx); 11798 | MEM_LOAD_64_ZTS ldr, TMP1, executor_globals, symbol_table.arData, REG1 11799 | add REG0, REG0, TMP1 11800 | IF_NOT_Z_TYPE REG0, IS_REFERENCE, >9, TMP1w 11801 | // (EXPECTED(p->key == varname)) 11802 | ldr TMP1, [REG0, #offsetof(Bucket, key)] 11803 | LOAD_ADDR TMP2, varname 11804 | cmp TMP1, TMP2 11805 | bne >9 11806 | GET_Z_PTR REG0, REG0 11807 | GC_ADDREF REG0, TMP1w 11808 |1: 11809 if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { 11810 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 11811 | // if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) 11812 | IF_ZVAL_REFCOUNTED op1_addr, >2, ZREG_TMP1, ZREG_TMP2 11813 |.cold_code 11814 |2: 11815 } 11816 | // zend_refcounted *garbage = Z_COUNTED_P(variable_ptr); 11817 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 11818 | // ZVAL_REF(variable_ptr, ref) 11819 | SET_ZVAL_PTR op1_addr, REG0, TMP1 11820 | SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2 11821 | // if (GC_DELREF(garbage) == 0) 11822 | GC_DELREF FCARG1x, TMP1w 11823 if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) { 11824 | bne >3 11825 } else { 11826 | bne >5 11827 } 11828 | ZVAL_DTOR_FUNC op1_info, opline, TMP1 11829 | b >5 11830 if (op1_info & (MAY_BE_REF|MAY_BE_ARRAY|MAY_BE_OBJECT)) { 11831 |3: 11832 | // GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) 11833 | IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w 11834 | EXT_CALL gc_possible_root, REG0 11835 | b >5 11836 } 11837 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 11838 |.code 11839 } 11840 } 11841 11842 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 11843 | // ZVAL_REF(variable_ptr, ref) 11844 | SET_ZVAL_PTR op1_addr, REG0, TMP1 11845 | SET_ZVAL_TYPE_INFO op1_addr, IS_REFERENCE_EX, TMP1w, TMP2 11846 } 11847 |5: 11848 //END of handler 11849 11850 |.cold_code 11851 |9: 11852 | LOAD_ADDR FCARG1x, (ptrdiff_t)varname 11853 if (opline->extended_value) { 11854 | ADD_SUB_64_WITH_CONST_32 add, FCARG2x, FCARG2x, opline->extended_value, TMP1 11855 } 11856 | EXT_CALL zend_jit_fetch_global_helper, REG0 11857 | mov REG0, RETVALx 11858 | b <1 11859 |.code 11860 11861 return 1; 11862} 11863 11864static int zend_jit_verify_arg_type(dasm_State **Dst, const zend_op *opline, zend_arg_info *arg_info, bool check_exception) 11865{ 11866 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 11867 bool in_cold = 0; 11868 uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY; 11869 zend_reg tmp_reg = (type_mask == 0 || is_power_of_two(type_mask)) ? ZREG_FCARG1 : ZREG_REG0; 11870 11871 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 11872 && JIT_G(current_frame) 11873 && JIT_G(current_frame)->prev) { 11874 zend_jit_trace_stack *stack = JIT_G(current_frame)->stack; 11875 uint8_t type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)); 11876 11877 if (type != IS_UNKNOWN && (type_mask & (1u << type))) { 11878 return 1; 11879 } 11880 } 11881 11882 if (ZEND_ARG_SEND_MODE(arg_info)) { 11883 if (opline->opcode == ZEND_RECV_INIT) { 11884 | LOAD_ZVAL_ADDR Rx(tmp_reg), res_addr 11885 | ZVAL_DEREF Rx(tmp_reg), MAY_BE_REF, TMP1w 11886 res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, 0); 11887 } else { 11888 | GET_ZVAL_PTR Rx(tmp_reg), res_addr, TMP1 11889 res_addr = ZEND_ADDR_MEM_ZVAL(tmp_reg, offsetof(zend_reference, val)); 11890 } 11891 } 11892 11893 if (type_mask != 0) { 11894 if (is_power_of_two(type_mask)) { 11895 uint32_t type_code = concrete_type(type_mask); 11896 | IF_NOT_ZVAL_TYPE res_addr, type_code, >1, ZREG_TMP1 11897 } else { 11898 | mov REG2w, #1 11899 | MEM_ACCESS_8_WITH_UOFFSET ldrb, REG1w, Rx(Z_REG(res_addr)), Z_OFFSET(res_addr)+offsetof(zval, u1.v.type), TMP1 11900 | lsl REG2w, REG2w, REG1w 11901 | TST_32_WITH_CONST REG2w, type_mask, TMP1w 11902 | beq >1 11903 } 11904 11905 |.cold_code 11906 |1: 11907 11908 in_cold = 1; 11909 } 11910 11911 if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { 11912 | LOAD_ZVAL_ADDR FCARG1x, res_addr 11913 } 11914 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 11915 | SET_EX_OPLINE opline, REG0 11916 } else { 11917 | ADDR_STORE EX->opline, opline, REG0 11918 } 11919 | LOAD_ADDR FCARG2x, (ptrdiff_t)arg_info 11920 | EXT_CALL zend_jit_verify_arg_slow, REG0 11921 11922 if (check_exception) { 11923 | GET_LOW_8BITS REG0w, RETVALw 11924 if (in_cold) { 11925 | cbnz REG0w, >1 11926 | b ->exception_handler 11927 |.code 11928 |1: 11929 } else { 11930 | cbz REG0w, ->exception_handler 11931 } 11932 } else if (in_cold) { 11933 | b >1 11934 |.code 11935 |1: 11936 } 11937 11938 return 1; 11939} 11940 11941static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array) 11942{ 11943 uint32_t arg_num = opline->op1.num; 11944 zend_arg_info *arg_info = NULL; 11945 11946 if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) { 11947 if (EXPECTED(arg_num <= op_array->num_args)) { 11948 arg_info = &op_array->arg_info[arg_num-1]; 11949 } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) { 11950 arg_info = &op_array->arg_info[op_array->num_args]; 11951 } 11952 if (arg_info && !ZEND_TYPE_IS_SET(arg_info->type)) { 11953 arg_info = NULL; 11954 } 11955 } 11956 11957 if (arg_info || (opline+1)->opcode != ZEND_RECV) { 11958 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 11959 if (!JIT_G(current_frame) || 11960 TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) < 0 || 11961 arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) { 11962 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 11963 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 11964 11965 if (!exit_addr) { 11966 return 0; 11967 } 11968 | ldr TMP1w, EX->This.u2.num_args 11969 | CMP_32_WITH_CONST TMP1w, arg_num, TMP2w 11970 | blo &exit_addr 11971 } 11972 } else { 11973 | ldr TMP1w, EX->This.u2.num_args 11974 | CMP_32_WITH_CONST TMP1w, arg_num, TMP2w 11975 | blo >1 11976 |.cold_code 11977 |1: 11978 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 11979 | SET_EX_OPLINE opline, REG0 11980 } else { 11981 | ADDR_STORE EX->opline, opline, REG0 11982 } 11983 | mov FCARG1x, FP 11984 | EXT_CALL zend_missing_arg_error, REG0 11985 | b ->exception_handler 11986 |.code 11987 } 11988 } 11989 11990 if (arg_info) { 11991 if (!zend_jit_verify_arg_type(Dst, opline, arg_info, 1)) { 11992 return 0; 11993 } 11994 } 11995 11996 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) { 11997 if ((opline+1)->opcode != ZEND_RECV && (opline+1)->opcode != ZEND_RECV_INIT) { 11998 | LOAD_IP_ADDR (opline + 1) 11999 zend_jit_set_last_valid_opline(opline + 1); 12000 } 12001 } 12002 12003 return 1; 12004} 12005 12006static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool is_last, int may_throw) 12007{ 12008 uint32_t arg_num = opline->op1.num; 12009 zval *zv = RT_CONSTANT(opline, opline->op2); 12010 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 12011 12012 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 12013 && JIT_G(current_frame) 12014 && TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) >= 0) { 12015 if (arg_num > TRACE_FRAME_NUM_ARGS(JIT_G(current_frame))) { 12016 | ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0 12017 if (Z_REFCOUNTED_P(zv)) { 12018 | ADDREF_CONST zv, REG0, TMP1 12019 } 12020 } 12021 } else { 12022 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || 12023 (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { 12024 | ldr TMP1w, EX->This.u2.num_args 12025 | CMP_32_WITH_CONST TMP1w, arg_num, TMP2w 12026 | bhs >5 12027 } 12028 | ZVAL_COPY_CONST res_addr, -1, -1, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0 12029 if (Z_REFCOUNTED_P(zv)) { 12030 | ADDREF_CONST zv, REG0, TMP1 12031 } 12032 } 12033 12034 if (Z_CONSTANT_P(zv)) { 12035 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 12036 | SET_EX_OPLINE opline, REG0 12037 } else { 12038 | ADDR_STORE EX->opline, opline, REG0 12039 } 12040 | LOAD_ZVAL_ADDR FCARG1x, res_addr 12041 | ldr REG0, EX->func 12042 | ldr FCARG2x, [REG0, #offsetof(zend_op_array, scope)] 12043 | EXT_CALL zval_update_constant_ex, REG0 12044 | cbnz RETVALw, >1 12045 |.cold_code 12046 |1: 12047 | ZVAL_PTR_DTOR res_addr, MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN, 1, 0, opline, ZREG_TMP1, ZREG_TMP2 12048 | SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF, TMP1w, TMP2 12049 | b ->exception_handler 12050 |.code 12051 } 12052 12053 |5: 12054 12055 if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) { 12056 do { 12057 zend_arg_info *arg_info; 12058 12059 if (arg_num <= op_array->num_args) { 12060 arg_info = &op_array->arg_info[arg_num-1]; 12061 } else if (op_array->fn_flags & ZEND_ACC_VARIADIC) { 12062 arg_info = &op_array->arg_info[op_array->num_args]; 12063 } else { 12064 break; 12065 } 12066 if (!ZEND_TYPE_IS_SET(arg_info->type)) { 12067 break; 12068 } 12069 if (!zend_jit_verify_arg_type(Dst, opline, arg_info, may_throw)) { 12070 return 0; 12071 } 12072 } while (0); 12073 } 12074 12075 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) { 12076 if (is_last) { 12077 | LOAD_IP_ADDR (opline + 1) 12078 zend_jit_set_last_valid_opline(opline + 1); 12079 } 12080 } 12081 return 1; 12082} 12083 12084static int zend_jit_class_guard(dasm_State **Dst, const zend_op *opline, zend_class_entry *ce) 12085{ 12086 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 12087 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12088 12089 if (!exit_addr) { 12090 return 0; 12091 } 12092 12093 | LOAD_ADDR TMP1, ((ptrdiff_t)ce) 12094 | ldr TMP2, [FCARG1x, #offsetof(zend_object, ce)] 12095 | cmp TMP2, TMP1 12096 | bne &exit_addr 12097 12098 return 1; 12099} 12100 12101static int zend_jit_fetch_obj(dasm_State **Dst, 12102 const zend_op *opline, 12103 const zend_op_array *op_array, 12104 zend_ssa *ssa, 12105 const zend_ssa_op *ssa_op, 12106 uint32_t op1_info, 12107 zend_jit_addr op1_addr, 12108 bool op1_indirect, 12109 zend_class_entry *ce, 12110 bool ce_is_instanceof, 12111 bool on_this, 12112 bool delayed_fetch_this, 12113 bool op1_avoid_refcounting, 12114 zend_class_entry *trace_ce, 12115 uint8_t prop_type, 12116 int may_throw) 12117{ 12118 zval *member; 12119 zend_property_info *prop_info; 12120 bool may_be_dynamic = 1; 12121 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 12122 zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); 12123 zend_jit_addr prop_addr; 12124 uint32_t res_info = RES_INFO(); 12125 bool type_loaded = 0; 12126 12127 ZEND_ASSERT(opline->op2_type == IS_CONST); 12128 ZEND_ASSERT(op1_info & MAY_BE_OBJECT); 12129 12130 member = RT_CONSTANT(opline, opline->op2); 12131 ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); 12132 prop_info = zend_get_known_property_info(op_array, ce, Z_STR_P(member), on_this, op_array->filename); 12133 12134 if (on_this) { 12135 | GET_ZVAL_PTR FCARG1x, this_addr, TMP1 12136 } else { 12137 if (opline->op1_type == IS_VAR 12138 && opline->opcode == ZEND_FETCH_OBJ_W 12139 && (op1_info & MAY_BE_INDIRECT) 12140 && Z_REG(op1_addr) == ZREG_FP) { 12141 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 12142 | IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w 12143 | GET_Z_PTR FCARG1x, FCARG1x 12144 |1: 12145 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12146 } 12147 if (op1_info & MAY_BE_REF) { 12148 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 12149 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 12150 } 12151 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 12152 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12153 } 12154 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) { 12155 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 12156 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 12157 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12158 12159 if (!exit_addr) { 12160 return 0; 12161 } 12162 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1 12163 } else { 12164 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >7, ZREG_TMP1 12165 } 12166 } 12167 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 12168 } 12169 12170 if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { 12171 prop_info = zend_get_known_property_info(op_array, trace_ce, Z_STR_P(member), on_this, op_array->filename); 12172 if (prop_info) { 12173 ce = trace_ce; 12174 ce_is_instanceof = 0; 12175 if (!(op1_info & MAY_BE_CLASS_GUARD)) { 12176 if (on_this && JIT_G(current_frame) 12177 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) { 12178 ZEND_ASSERT(JIT_G(current_frame)->ce == ce); 12179 } else if (zend_jit_class_guard(Dst, opline, ce)) { 12180 if (on_this && JIT_G(current_frame)) { 12181 JIT_G(current_frame)->ce = ce; 12182 TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame)); 12183 } 12184 } else { 12185 return 0; 12186 } 12187 if (ssa->var_info && ssa_op->op1_use >= 0) { 12188 ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; 12189 ssa->var_info[ssa_op->op1_use].ce = ce; 12190 ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof; 12191 } 12192 } 12193 } 12194 } 12195 12196 if (!prop_info) { 12197 | ldr REG0, EX->run_time_cache 12198 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS), TMP1 12199 | ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)] 12200 | cmp REG2, TMP1 12201 | bne >5 12202 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, ((opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*)), TMP1 12203 may_be_dynamic = zend_may_be_dynamic_property(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array); 12204 if (may_be_dynamic) { 12205 | tst REG0, REG0 12206 if (opline->opcode == ZEND_FETCH_OBJ_W) { 12207 | blt >5 12208 } else { 12209 | blt >8 // dynamic property 12210 } 12211 } 12212 | add TMP1, FCARG1x, REG0 12213 | ldr REG2w, [TMP1, #offsetof(zval,u1.type_info)] 12214 | IF_UNDEF REG2w, >5 12215 | mov FCARG1x, TMP1 12216 type_loaded = 1; 12217 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12218 if (opline->opcode == ZEND_FETCH_OBJ_W 12219 && (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT)))) { 12220 uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; 12221 12222 | ldr REG0, EX->run_time_cache 12223 | MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, ((opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2), TMP1 12224 | cbnz FCARG2x, >1 12225 |.cold_code 12226 |1: 12227 | ldr TMP1w, [FCARG2x, #offsetof(zend_property_info, flags)] 12228 | tst TMP1w, #ZEND_ACC_READONLY 12229 if (flags) { 12230 | beq >3 12231 } else { 12232 | beq >4 12233 } 12234 | IF_NOT_TYPE REG2w, IS_OBJECT_EX, >2 12235 | GET_Z_PTR REG2, FCARG1x 12236 | GC_ADDREF REG2, TMP1w 12237 | SET_ZVAL_PTR res_addr, REG2, TMP1 12238 | SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX, TMP1w, TMP2 12239 | b >9 12240 |2: 12241 | mov FCARG1x, FCARG2x 12242 | SET_EX_OPLINE opline, REG0 12243 | EXT_CALL zend_readonly_property_modification_error, REG0 12244 | SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR, TMP1w, TMP2 12245 | b >9 12246 |3: 12247 if (flags == ZEND_FETCH_DIM_WRITE) { 12248 | SET_EX_OPLINE opline, REG0 12249 | EXT_CALL zend_jit_check_array_promotion, REG0 12250 | b >9 12251 } else if (flags == ZEND_FETCH_REF) { 12252 | LOAD_ZVAL_ADDR CARG3, res_addr 12253 | EXT_CALL zend_jit_create_typed_ref, REG0 12254 | b >9 12255 } else { 12256 ZEND_ASSERT(flags == 0); 12257 } 12258 |.code 12259 |4: 12260 } 12261 } else { 12262 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset); 12263 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 12264 if (opline->opcode == ZEND_FETCH_OBJ_W || !(res_info & MAY_BE_GUARD) || !JIT_G(current_frame)) { 12265 /* perform IS_UNDEF check only after result type guard (during deoptimization) */ 12266 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 12267 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12268 12269 if (!exit_addr) { 12270 return 0; 12271 } 12272 type_loaded = 1; 12273 | MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1 12274 | IF_UNDEF REG2w, &exit_addr 12275 } 12276 } else { 12277 type_loaded = 1; 12278 | MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1 12279 | IF_UNDEF REG2w, >5 12280 } 12281 if (opline->opcode == ZEND_FETCH_OBJ_W && (prop_info->flags & ZEND_ACC_READONLY)) { 12282 if (!type_loaded) { 12283 type_loaded = 1; 12284 | MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1 12285 } 12286 | IF_NOT_TYPE REG2w, IS_OBJECT_EX, >4 12287 | GET_ZVAL_PTR REG2, prop_addr, TMP1 12288 | GC_ADDREF REG2, TMP1w 12289 | SET_ZVAL_PTR res_addr, REG2, TMP1 12290 | SET_ZVAL_TYPE_INFO res_addr, IS_OBJECT_EX, TMP1w, TMP2 12291 | b >9 12292 |.cold_code 12293 |4: 12294 | LOAD_ADDR FCARG1x, prop_info 12295 | SET_EX_OPLINE opline, REG0 12296 | EXT_CALL zend_readonly_property_modification_error, REG0 12297 | SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR, TMP1w, TMP2 12298 | b >9 12299 |.code 12300 } 12301 if (opline->opcode == ZEND_FETCH_OBJ_W 12302 && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS) 12303 && ZEND_TYPE_IS_SET(prop_info->type)) { 12304 uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; 12305 12306 if (flags == ZEND_FETCH_DIM_WRITE) { 12307 if ((ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_ARRAY) == 0) { 12308 if (!type_loaded) { 12309 type_loaded = 1; 12310 | MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1 12311 } 12312 | cmp REG2w, #IS_FALSE 12313 | ble >1 12314 |.cold_code 12315 |1: 12316 if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) { 12317 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 12318 } 12319 | LOAD_ADDR FCARG2x, prop_info 12320 | SET_EX_OPLINE opline, REG0 12321 | EXT_CALL zend_jit_check_array_promotion, REG0 12322 | b >9 12323 |.code 12324 } 12325 } else if (flags == ZEND_FETCH_REF) { 12326 if (!type_loaded) { 12327 type_loaded = 1; 12328 | MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1 12329 } 12330 | GET_LOW_8BITS TMP1w, REG2w 12331 | IF_TYPE TMP1w, IS_REFERENCE, >1 12332 if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) { 12333 | LOAD_ADDR FCARG2x, prop_info 12334 } else { 12335 int prop_info_offset = 12336 (((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*)); 12337 12338 | ldr REG0, [FCARG1x, #offsetof(zend_object, ce)] 12339 | ldr REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)] 12340 | MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1 12341 } 12342 if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) { 12343 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 12344 } 12345 | LOAD_ZVAL_ADDR CARG3, res_addr 12346 | EXT_CALL zend_jit_create_typed_ref, REG0 12347 | b >9 12348 |1: 12349 } else { 12350 ZEND_UNREACHABLE(); 12351 } 12352 } 12353 } 12354 if (opline->opcode == ZEND_FETCH_OBJ_W) { 12355 if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) { 12356 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 12357 } 12358 | SET_ZVAL_PTR res_addr, FCARG1x, TMP1 12359 | SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT, TMP1w, TMP2 12360 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && prop_info) { 12361 ssa->var_info[ssa_op->result_def].indirect_reference = 1; 12362 } 12363 } else { 12364 bool result_avoid_refcounting = 0; 12365 12366 if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) { 12367 uint32_t flags = 0; 12368 uint32_t old_info; 12369 zend_jit_trace_stack *stack = JIT_G(current_frame)->stack; 12370 int32_t exit_point; 12371 const void *exit_addr; 12372 uint32_t type; 12373 zend_jit_addr val_addr = prop_addr; 12374 12375 if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) 12376 && !delayed_fetch_this 12377 && !op1_avoid_refcounting) { 12378 flags = ZEND_JIT_EXIT_FREE_OP1; 12379 } 12380 12381 if ((opline->result_type & (IS_VAR|IS_TMP_VAR)) 12382 && !(flags & ZEND_JIT_EXIT_FREE_OP1) 12383 && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) 12384 && (ssa_op+1)->op1_use == ssa_op->result_def 12385 && zend_jit_may_avoid_refcounting(opline+1, res_info)) { 12386 result_avoid_refcounting = 1; 12387 ssa->var_info[ssa_op->result_def].avoid_refcounting = 1; 12388 } 12389 12390 type = concrete_type(res_info); 12391 12392 if (prop_type != IS_UNKNOWN 12393 && prop_type != IS_UNDEF 12394 && prop_type != IS_REFERENCE 12395 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT) { 12396 exit_point = zend_jit_trace_get_exit_point(opline, 0); 12397 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12398 if (!exit_addr) { 12399 return 0; 12400 } 12401 } else { 12402 val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 12403 | LOAD_ZVAL_ADDR REG0, prop_addr 12404 if (op1_avoid_refcounting) { 12405 SET_STACK_REG(JIT_G(current_frame)->stack, 12406 EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE); 12407 } 12408 old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); 12409 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1); 12410 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0); 12411 exit_point = zend_jit_trace_get_exit_point(opline+1, flags); 12412 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info); 12413 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12414 if (!exit_addr) { 12415 return 0; 12416 } 12417 12418 if (!type_loaded) { 12419 type_loaded = 1; 12420 | MEM_ACCESS_32_WITH_UOFFSET ldr, REG2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.type_info)), TMP1 12421 } 12422 | // ZVAL_DEREF() 12423 | GET_LOW_8BITS TMP1w, REG2w 12424 | IF_NOT_TYPE TMP1w, IS_REFERENCE, >1 12425 | GET_Z_PTR REG0, REG0 12426 | add REG0, REG0, #offsetof(zend_reference, val) 12427 | GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1 12428 } 12429 res_info &= ~MAY_BE_GUARD; 12430 ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; 12431 if (type < IS_STRING) { 12432 |1: 12433 if (type_loaded) { 12434 | IF_NOT_TYPE REG2w, type, &exit_addr 12435 } else { 12436 | IF_NOT_ZVAL_TYPE val_addr, type, &exit_addr, ZREG_TMP1 12437 } 12438 } else { 12439 if (!type_loaded) { 12440 type_loaded = 1; 12441 | GET_ZVAL_TYPE_INFO REG2w, val_addr, TMP1 12442 } 12443 |1: 12444 | GET_LOW_8BITS TMP1w, REG2w 12445 | IF_NOT_TYPE TMP1w, type, &exit_addr 12446 } 12447 | // ZVAL_COPY 12448 | ZVAL_COPY_VALUE_V res_addr, -1, val_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0 12449 if (type < IS_STRING) { 12450 if (Z_REG(res_addr) != ZREG_FP || 12451 JIT_G(current_frame) == NULL || 12452 STACK_MEM_TYPE(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(Z_OFFSET(res_addr))) != type) { 12453 | SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2 12454 } 12455 } else { 12456 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1 12457 if (!result_avoid_refcounting) { 12458 | TRY_ADDREF res_info, REG2w, REG1, TMP1w 12459 } 12460 } 12461 } else { 12462 if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_REG2)) { 12463 return 0; 12464 } 12465 } 12466 } 12467 12468 if (op1_avoid_refcounting) { 12469 SET_STACK_REG(JIT_G(current_frame)->stack, 12470 EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE); 12471 } 12472 12473 |.cold_code 12474 12475 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || !prop_info) { 12476 |5: 12477 | SET_EX_OPLINE opline, REG0 12478 if (opline->opcode == ZEND_FETCH_OBJ_W) { 12479 | EXT_CALL zend_jit_fetch_obj_w_slow, REG0 12480 } else if (opline->opcode != ZEND_FETCH_OBJ_IS) { 12481 | EXT_CALL zend_jit_fetch_obj_r_slow, REG0 12482 } else { 12483 | EXT_CALL zend_jit_fetch_obj_is_slow, REG0 12484 } 12485 | b >9 12486 } 12487 12488 if ((op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)- MAY_BE_OBJECT)) && JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) { 12489 |7: 12490 if (opline->opcode != ZEND_FETCH_OBJ_IS) { 12491 | SET_EX_OPLINE opline, REG0 12492 if (opline->opcode != ZEND_FETCH_OBJ_W 12493 && (op1_info & MAY_BE_UNDEF)) { 12494 zend_jit_addr orig_op1_addr = OP1_ADDR(); 12495 12496 if (op1_info & MAY_BE_ANY) { 12497 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1, ZREG_TMP1 12498 } 12499 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 12500 | EXT_CALL zend_jit_undefined_op_helper, REG0 12501 |1: 12502 | LOAD_ZVAL_ADDR FCARG1x, orig_op1_addr 12503 } else if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 12504 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 12505 } 12506 | LOAD_ADDR FCARG2x, Z_STRVAL_P(member) 12507 if (opline->opcode == ZEND_FETCH_OBJ_W) { 12508 | EXT_CALL zend_jit_invalid_property_write, REG0 12509 | SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR, TMP1w, TMP2 12510 } else { 12511 | EXT_CALL zend_jit_invalid_property_read, REG0 12512 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 12513 } 12514 | b >9 12515 } else { 12516 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 12517 | b >9 12518 } 12519 } 12520 12521 if (!prop_info 12522 && may_be_dynamic 12523 && opline->opcode != ZEND_FETCH_OBJ_W) { 12524 |8: 12525 | mov FCARG2x, REG0 12526 | SET_EX_OPLINE opline, REG0 12527 if (opline->opcode != ZEND_FETCH_OBJ_IS) { 12528 | EXT_CALL zend_jit_fetch_obj_r_dynamic, REG0 12529 } else { 12530 | EXT_CALL zend_jit_fetch_obj_is_dynamic, REG0 12531 } 12532 | b >9 12533 } 12534 12535 |.code; 12536 |9: // END 12537 if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { 12538 if (opline->op1_type == IS_VAR 12539 && opline->opcode == ZEND_FETCH_OBJ_W 12540 && (op1_info & MAY_BE_RC1)) { 12541 zend_jit_addr orig_op1_addr = OP1_ADDR(); 12542 12543 | IF_NOT_ZVAL_REFCOUNTED orig_op1_addr, >1, ZREG_TMP1, ZREG_TMP2 12544 | GET_ZVAL_PTR FCARG1x, orig_op1_addr, TMP1 12545 | GC_DELREF FCARG1x, TMP1w 12546 | bne >1 12547 | SET_EX_OPLINE opline, REG0 12548 | EXT_CALL zend_jit_extract_helper, REG0 12549 |1: 12550 } else if (!op1_avoid_refcounting) { 12551 if (on_this) { 12552 op1_info &= ~MAY_BE_RC1; 12553 } 12554 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 12555 } 12556 } 12557 12558 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 12559 && prop_info 12560 && (opline->opcode != ZEND_FETCH_OBJ_W || 12561 !(opline->extended_value & ZEND_FETCH_OBJ_FLAGS) || 12562 !ZEND_TYPE_IS_SET(prop_info->type)) 12563 && (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || on_this || op1_indirect)) { 12564 may_throw = 0; 12565 } 12566 12567 if (may_throw) { 12568 if (!zend_jit_check_exception(Dst)) { 12569 return 0; 12570 } 12571 } 12572 12573 return 1; 12574} 12575 12576static int zend_jit_incdec_obj(dasm_State **Dst, 12577 const zend_op *opline, 12578 const zend_op_array *op_array, 12579 zend_ssa *ssa, 12580 const zend_ssa_op *ssa_op, 12581 uint32_t op1_info, 12582 zend_jit_addr op1_addr, 12583 bool op1_indirect, 12584 zend_class_entry *ce, 12585 bool ce_is_instanceof, 12586 bool on_this, 12587 bool delayed_fetch_this, 12588 zend_class_entry *trace_ce, 12589 uint8_t prop_type) 12590{ 12591 zval *member; 12592 zend_string *name; 12593 zend_property_info *prop_info; 12594 zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); 12595 zend_jit_addr res_addr = 0; 12596 zend_jit_addr prop_addr; 12597 bool needs_slow_path = 0; 12598 bool use_prop_guard = 0; 12599 bool may_throw = 0; 12600 uint32_t res_info = (opline->result_type != IS_UNDEF) ? RES_INFO() : 0; 12601 12602 ZEND_ASSERT(opline->op2_type == IS_CONST); 12603 ZEND_ASSERT(op1_info & MAY_BE_OBJECT); 12604 12605 if (opline->result_type != IS_UNUSED) { 12606 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 12607 } 12608 12609 member = RT_CONSTANT(opline, opline->op2); 12610 ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); 12611 name = Z_STR_P(member); 12612 prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename); 12613 12614 if (on_this) { 12615 | GET_ZVAL_PTR FCARG1x, this_addr, TMP1 12616 } else { 12617 if (opline->op1_type == IS_VAR 12618 && (op1_info & MAY_BE_INDIRECT) 12619 && Z_REG(op1_addr) == ZREG_FP) { 12620 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 12621 | IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w 12622 | GET_Z_PTR FCARG1x, FCARG1x 12623 |1: 12624 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12625 } 12626 if (op1_info & MAY_BE_REF) { 12627 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 12628 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 12629 } 12630 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 12631 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12632 } 12633 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) { 12634 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 12635 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 12636 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12637 12638 if (!exit_addr) { 12639 return 0; 12640 } 12641 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1 12642 } else { 12643 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1 12644 |.cold_code 12645 |1: 12646 | SET_EX_OPLINE opline, REG0 12647 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 12648 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 12649 } 12650 | LOAD_ADDR FCARG2x, ZSTR_VAL(name) 12651 | EXT_CALL zend_jit_invalid_property_incdec, REG0 12652 | b ->exception_handler 12653 |.code 12654 } 12655 } 12656 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 12657 } 12658 12659 if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { 12660 prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename); 12661 if (prop_info) { 12662 ce = trace_ce; 12663 ce_is_instanceof = 0; 12664 if (!(op1_info & MAY_BE_CLASS_GUARD)) { 12665 if (on_this && JIT_G(current_frame) 12666 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) { 12667 ZEND_ASSERT(JIT_G(current_frame)->ce == ce); 12668 } else if (zend_jit_class_guard(Dst, opline, ce)) { 12669 if (on_this && JIT_G(current_frame)) { 12670 JIT_G(current_frame)->ce = ce; 12671 TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame)); 12672 } 12673 } else { 12674 return 0; 12675 } 12676 if (ssa->var_info && ssa_op->op1_use >= 0) { 12677 ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; 12678 ssa->var_info[ssa_op->op1_use].ce = ce; 12679 ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof; 12680 } 12681 if (ssa->var_info && ssa_op->op1_def >= 0) { 12682 ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD; 12683 ssa->var_info[ssa_op->op1_def].ce = ce; 12684 ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof; 12685 } 12686 } 12687 } 12688 } 12689 12690 use_prop_guard = (prop_type != IS_UNKNOWN 12691 && prop_type != IS_UNDEF 12692 && prop_type != IS_REFERENCE 12693 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT); 12694 12695 if (!prop_info) { 12696 needs_slow_path = 1; 12697 12698 | ldr REG0, EX->run_time_cache 12699 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, opline->extended_value, TMP1 12700 | ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)] 12701 | cmp REG2, TMP1 12702 | bne >7 12703 if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) { 12704 | MEM_ACCESS_64_WITH_UOFFSET ldr, TMP1, REG0, (opline->extended_value + sizeof(void*) * 2), TMP1 12705 | cbnz TMP1, >7 12706 } 12707 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->extended_value + sizeof(void*)), TMP1 12708 | tst REG0, REG0 12709 | blt >7 12710 | add TMP1, FCARG1x, REG0 12711 if (!use_prop_guard) { 12712 | ldrb TMP2w, [TMP1, #offsetof(zval,u1.v.type)] 12713 | IF_TYPE TMP2w , IS_UNDEF, >7 12714 } 12715 | mov FCARG1x, TMP1 12716 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12717 } else { 12718 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset); 12719 if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) { 12720 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 12721 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 12722 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12723 12724 if (!exit_addr) { 12725 return 0; 12726 } 12727 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1 12728 | IF_TYPE TMP2w, IS_UNDEF, &exit_addr 12729 } else { 12730 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1 12731 | IF_TYPE TMP2w, IS_UNDEF, >7 12732 needs_slow_path = 1; 12733 } 12734 } 12735 if (ZEND_TYPE_IS_SET(prop_info->type)) { 12736 may_throw = 1; 12737 | SET_EX_OPLINE opline, REG0 12738 if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) { 12739 | LOAD_ADDR FCARG2x, prop_info 12740 } else { 12741 int prop_info_offset = 12742 (((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*)); 12743 12744 | ldr REG0, [FCARG1x, #offsetof(zend_object, ce)] 12745 | ldr REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)] 12746 | MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1 12747 } 12748 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 12749 if (opline->result_type == IS_UNUSED) { 12750 switch (opline->opcode) { 12751 case ZEND_PRE_INC_OBJ: 12752 case ZEND_POST_INC_OBJ: 12753 | EXT_CALL zend_jit_inc_typed_prop, REG0 12754 break; 12755 case ZEND_PRE_DEC_OBJ: 12756 case ZEND_POST_DEC_OBJ: 12757 | EXT_CALL zend_jit_dec_typed_prop, REG0 12758 break; 12759 default: 12760 ZEND_UNREACHABLE(); 12761 } 12762 } else { 12763 | LOAD_ZVAL_ADDR CARG3, res_addr 12764 switch (opline->opcode) { 12765 case ZEND_PRE_INC_OBJ: 12766 | EXT_CALL zend_jit_pre_inc_typed_prop, REG0 12767 break; 12768 case ZEND_PRE_DEC_OBJ: 12769 | EXT_CALL zend_jit_pre_dec_typed_prop, REG0 12770 break; 12771 case ZEND_POST_INC_OBJ: 12772 | EXT_CALL zend_jit_post_inc_typed_prop, REG0 12773 break; 12774 case ZEND_POST_DEC_OBJ: 12775 | EXT_CALL zend_jit_post_dec_typed_prop, REG0 12776 break; 12777 default: 12778 ZEND_UNREACHABLE(); 12779 } 12780 } 12781 } 12782 } 12783 12784 if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) { 12785 uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN; 12786 zend_jit_addr var_addr = prop_addr; 12787 12788 if (use_prop_guard) { 12789 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 12790 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12791 if (!exit_addr) { 12792 return 0; 12793 } 12794 12795 | IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr, ZREG_TMP1 12796 var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)); 12797 } 12798 12799 if (var_info & MAY_BE_REF) { 12800 may_throw = 1; 12801 var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12802 if (Z_REG(prop_addr) != ZREG_FCARG1 || Z_OFFSET(prop_addr) != 0) { 12803 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 12804 } 12805 | IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2, ZREG_TMP1 12806 | GET_ZVAL_PTR FCARG1x, var_addr, TMP1 12807 | ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)] 12808 | cbnz TMP1, >1 12809 | add FCARG1x, FCARG1x, #offsetof(zend_reference, val) 12810 |.cold_code 12811 |1: 12812 if (opline) { 12813 | SET_EX_OPLINE opline, REG0 12814 } 12815 if (opline->result_type == IS_UNUSED) { 12816 | mov FCARG2x, xzr 12817 } else { 12818 | LOAD_ZVAL_ADDR FCARG2x, res_addr 12819 } 12820 switch (opline->opcode) { 12821 case ZEND_PRE_INC_OBJ: 12822 | EXT_CALL zend_jit_pre_inc_typed_ref, REG0 12823 break; 12824 case ZEND_PRE_DEC_OBJ: 12825 | EXT_CALL zend_jit_pre_dec_typed_ref, REG0 12826 break; 12827 case ZEND_POST_INC_OBJ: 12828 | EXT_CALL zend_jit_post_inc_typed_ref, REG0 12829 break; 12830 case ZEND_POST_DEC_OBJ: 12831 | EXT_CALL zend_jit_post_dec_typed_ref, REG0 12832 break; 12833 default: 12834 ZEND_UNREACHABLE(); 12835 } 12836 | b >9 12837 |.code 12838 |2: 12839 } 12840 12841 if (var_info & MAY_BE_LONG) { 12842 if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) { 12843 | IF_NOT_ZVAL_TYPE var_addr, IS_LONG, >2, ZREG_TMP1 12844 } 12845 if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) { 12846 if (opline->result_type != IS_UNUSED) { 12847 | ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_REG1, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 12848 } 12849 } 12850 if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) { 12851 | LONG_ADD_SUB_WITH_IMM adds, var_addr, Z_L(1), TMP1, TMP2 12852 } else { 12853 | LONG_ADD_SUB_WITH_IMM subs, var_addr, Z_L(1), TMP1, TMP2 12854 } 12855 | bvs >3 12856 if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) { 12857 if (opline->result_type != IS_UNUSED) { 12858 | ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_LONG, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 12859 } 12860 } 12861 |.cold_code 12862 } 12863 if (var_info & (MAY_BE_ANY - MAY_BE_LONG)) { 12864 if (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 12865 may_throw = 1; 12866 } 12867 if (var_info & MAY_BE_LONG) { 12868 |2: 12869 } 12870 if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) { 12871 var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 12872 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 12873 } 12874 if (opline->opcode == ZEND_POST_INC_OBJ || opline->opcode == ZEND_POST_DEC_OBJ) { 12875 | ZVAL_COPY_VALUE res_addr, -1, var_addr, MAY_BE_ANY, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 12876 | TRY_ADDREF MAY_BE_ANY, REG0w, REG2, TMP1w 12877 } 12878 if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) { 12879 if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) { 12880 | LOAD_ZVAL_ADDR FCARG2x, res_addr 12881 | EXT_CALL zend_jit_pre_inc, REG0 12882 } else { 12883 | EXT_CALL increment_function, REG0 12884 } 12885 } else { 12886 if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) { 12887 | LOAD_ZVAL_ADDR FCARG2x, res_addr 12888 | EXT_CALL zend_jit_pre_dec, REG0 12889 } else { 12890 | EXT_CALL decrement_function, REG0 12891 } 12892 } 12893 if (var_info & MAY_BE_LONG) { 12894 | b >4 12895 } 12896 } 12897 12898 if (var_info & MAY_BE_LONG) { 12899 |3: 12900 if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_POST_INC_OBJ) { 12901 uint64_t val = 0x43e0000000000000; 12902 | LOAD_64BIT_VAL TMP2, val 12903 | SET_ZVAL_LVAL_FROM_REG var_addr, TMP2, TMP1 12904 | SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE, TMP1w, TMP2 12905 if (opline->opcode == ZEND_PRE_INC_OBJ && opline->result_type != IS_UNUSED) { 12906 | SET_ZVAL_LVAL_FROM_REG res_addr, TMP2, TMP1 12907 | SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2 12908 } 12909 } else { 12910 uint64_t val = 0xc3e0000000000000; 12911 | LOAD_64BIT_VAL TMP2, val 12912 | SET_ZVAL_LVAL_FROM_REG var_addr, TMP2, TMP1 12913 | SET_ZVAL_TYPE_INFO var_addr, IS_DOUBLE, TMP1w, TMP2 12914 if (opline->opcode == ZEND_PRE_DEC_OBJ && opline->result_type != IS_UNUSED) { 12915 | SET_ZVAL_LVAL_FROM_REG res_addr, TMP2, TMP1 12916 | SET_ZVAL_TYPE_INFO res_addr, IS_DOUBLE, TMP1w, TMP2 12917 } 12918 } 12919 if (opline->result_type != IS_UNUSED 12920 && (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) 12921 && prop_info 12922 && !ZEND_TYPE_IS_SET(prop_info->type) 12923 && (res_info & MAY_BE_GUARD) 12924 && (res_info & MAY_BE_LONG)) { 12925 zend_jit_trace_stack *stack = JIT_G(current_frame)->stack; 12926 uint32_t old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); 12927 int32_t exit_point; 12928 const void *exit_addr; 12929 12930 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0); 12931 exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); 12932 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 12933 if (!exit_addr) { 12934 return 0; 12935 } 12936 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info); 12937 ssa->var_info[ssa_op->result_def].type = res_info & ~MAY_BE_GUARD; 12938 | b &exit_addr 12939 |.code 12940 } else { 12941 | b >4 12942 |.code 12943 |4: 12944 } 12945 } 12946 } 12947 12948 if (needs_slow_path) { 12949 may_throw = 1; 12950 |.cold_code 12951 |7: 12952 | SET_EX_OPLINE opline, REG0 12953 | // value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value)); 12954 | LOAD_ADDR FCARG2x, name 12955 | ldr CARG3, EX->run_time_cache 12956 | ADD_SUB_64_WITH_CONST_32 add, CARG3, CARG3, opline->extended_value, TMP1 12957 if (opline->result_type == IS_UNUSED) { 12958 | mov CARG4, xzr 12959 } else { 12960 | LOAD_ZVAL_ADDR CARG4, res_addr 12961 } 12962 12963 switch (opline->opcode) { 12964 case ZEND_PRE_INC_OBJ: 12965 | EXT_CALL zend_jit_pre_inc_obj_helper, REG0 12966 break; 12967 case ZEND_PRE_DEC_OBJ: 12968 | EXT_CALL zend_jit_pre_dec_obj_helper, REG0 12969 break; 12970 case ZEND_POST_INC_OBJ: 12971 | EXT_CALL zend_jit_post_inc_obj_helper, REG0 12972 break; 12973 case ZEND_POST_DEC_OBJ: 12974 | EXT_CALL zend_jit_post_dec_obj_helper, REG0 12975 break; 12976 default: 12977 ZEND_UNREACHABLE(); 12978 } 12979 12980 | b >9 12981 |.code 12982 } 12983 12984 |9: 12985 if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { 12986 if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) { 12987 may_throw = 1; 12988 } 12989 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 12990 } 12991 12992 if (may_throw) { 12993 if (!zend_jit_check_exception(Dst)) { 12994 return 0; 12995 } 12996 } 12997 12998 return 1; 12999} 13000 13001static int zend_jit_assign_obj_op(dasm_State **Dst, 13002 const zend_op *opline, 13003 const zend_op_array *op_array, 13004 zend_ssa *ssa, 13005 const zend_ssa_op *ssa_op, 13006 uint32_t op1_info, 13007 zend_jit_addr op1_addr, 13008 uint32_t val_info, 13009 zend_ssa_range *val_range, 13010 bool op1_indirect, 13011 zend_class_entry *ce, 13012 bool ce_is_instanceof, 13013 bool on_this, 13014 bool delayed_fetch_this, 13015 zend_class_entry *trace_ce, 13016 uint8_t prop_type) 13017{ 13018 zval *member; 13019 zend_string *name; 13020 zend_property_info *prop_info; 13021 zend_jit_addr val_addr = OP1_DATA_ADDR(); 13022 zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); 13023 zend_jit_addr prop_addr; 13024 bool needs_slow_path = 0; 13025 bool use_prop_guard = 0; 13026 bool may_throw = 0; 13027 binary_op_type binary_op = get_binary_op(opline->extended_value); 13028 13029 ZEND_ASSERT(opline->op2_type == IS_CONST); 13030 ZEND_ASSERT(op1_info & MAY_BE_OBJECT); 13031 ZEND_ASSERT(opline->result_type == IS_UNUSED); 13032 13033 member = RT_CONSTANT(opline, opline->op2); 13034 ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); 13035 name = Z_STR_P(member); 13036 prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename); 13037 13038 if (on_this) { 13039 | GET_ZVAL_PTR FCARG1x, this_addr, TMP1 13040 } else { 13041 if (opline->op1_type == IS_VAR 13042 && (op1_info & MAY_BE_INDIRECT) 13043 && Z_REG(op1_addr) == ZREG_FP) { 13044 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 13045 | IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w 13046 | GET_Z_PTR FCARG1x, FCARG1x 13047 |1: 13048 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 13049 } 13050 if (op1_info & MAY_BE_REF) { 13051 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 13052 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 13053 } 13054 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 13055 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 13056 } 13057 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) { 13058 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 13059 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 13060 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 13061 13062 if (!exit_addr) { 13063 return 0; 13064 } 13065 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1 13066 } else { 13067 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1 13068 |.cold_code 13069 |1: 13070 | SET_EX_OPLINE opline, REG0 13071 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 13072 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 13073 } 13074 | LOAD_ADDR FCARG2x, ZSTR_VAL(name) 13075 if (op1_info & MAY_BE_UNDEF) { 13076 | EXT_CALL zend_jit_invalid_property_assign_op, REG0 13077 } else { 13078 | EXT_CALL zend_jit_invalid_property_assign, REG0 13079 } 13080 may_throw = 1; 13081 if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR)) 13082 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13083 | b >8 13084 } else { 13085 | b >9 13086 } 13087 |.code 13088 } 13089 } 13090 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 13091 } 13092 13093 if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { 13094 prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename); 13095 if (prop_info) { 13096 ce = trace_ce; 13097 ce_is_instanceof = 0; 13098 if (!(op1_info & MAY_BE_CLASS_GUARD)) { 13099 if (on_this && JIT_G(current_frame) 13100 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) { 13101 ZEND_ASSERT(JIT_G(current_frame)->ce == ce); 13102 } else if (zend_jit_class_guard(Dst, opline, ce)) { 13103 if (on_this && JIT_G(current_frame)) { 13104 JIT_G(current_frame)->ce = ce; 13105 TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame)); 13106 } 13107 } else { 13108 return 0; 13109 } 13110 if (ssa->var_info && ssa_op->op1_use >= 0) { 13111 ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; 13112 ssa->var_info[ssa_op->op1_use].ce = ce; 13113 ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof; 13114 } 13115 if (ssa->var_info && ssa_op->op1_def >= 0) { 13116 ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD; 13117 ssa->var_info[ssa_op->op1_def].ce = ce; 13118 ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof; 13119 } 13120 } 13121 } 13122 } 13123 13124 use_prop_guard = (prop_type != IS_UNKNOWN 13125 && prop_type != IS_UNDEF 13126 && prop_type != IS_REFERENCE 13127 && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT); 13128 13129 if (!prop_info) { 13130 needs_slow_path = 1; 13131 13132 | ldr REG0, EX->run_time_cache 13133 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, ((opline+1)->extended_value), TMP1 13134 | ldr TMP2, [FCARG1x, #offsetof(zend_object, ce)] 13135 | cmp REG2, TMP2 13136 | bne >7 13137 if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) { 13138 | MEM_ACCESS_64_WITH_UOFFSET ldr, TMP1, REG0, ((opline+1)->extended_value + sizeof(void*) * 2), TMP1 13139 | cbnz TMP1, >7 13140 } 13141 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, ((opline+1)->extended_value + sizeof(void*)), TMP1 13142 | tst REG0, REG0 13143 | blt >7 13144 | add TMP1, FCARG1x, REG0 13145 if (!use_prop_guard) { 13146 | ldrb TMP2w, [TMP1, #offsetof(zval,u1.v.type)] 13147 | IF_TYPE TMP2w, IS_UNDEF, >7 13148 } 13149 | mov FCARG1x, TMP1 13150 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 13151 } else { 13152 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset); 13153 if (ZEND_TYPE_IS_SET(prop_info->type) || !use_prop_guard) { 13154 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 13155 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 13156 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 13157 13158 if (!exit_addr) { 13159 return 0; 13160 } 13161 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1 13162 | IF_TYPE TMP2w, IS_UNDEF, &exit_addr 13163 } else { 13164 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1 13165 | IF_TYPE TMP2w, IS_UNDEF, >7 13166 needs_slow_path = 1; 13167 } 13168 } 13169 if (ZEND_TYPE_IS_SET(prop_info->type)) { 13170 uint32_t info = val_info; 13171 13172 may_throw = 1; 13173 13174 if (opline) { 13175 | SET_EX_OPLINE opline, REG0 13176 } 13177 13178 | IF_ZVAL_TYPE prop_addr, IS_REFERENCE, >1, ZREG_TMP1 13179 |.cold_code 13180 |1: 13181 | GET_ZVAL_PTR FCARG1x, prop_addr, TMP1 13182 if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) { 13183 | LOAD_ZVAL_ADDR FCARG2x, val_addr 13184 } 13185 | LOAD_ADDR CARG3, binary_op 13186 if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) 13187 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13188 | EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0 13189 } else { 13190 | EXT_CALL zend_jit_assign_op_to_typed_ref, REG0 13191 } 13192 | b >9 13193 |.code 13194 13195 | // value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC); 13196 13197 if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) { 13198 | LOAD_ADDR FCARG2x, prop_info 13199 } else { 13200 int prop_info_offset = 13201 (((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*)); 13202 13203 | ldr REG0, [FCARG1x, #offsetof(zend_object, ce)] 13204 | ldr REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)] 13205 | MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1 13206 } 13207 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 13208 | LOAD_ZVAL_ADDR CARG3, val_addr 13209 | LOAD_ADDR CARG4, binary_op 13210 13211 | EXT_CALL zend_jit_assign_op_to_typed_prop, REG0 13212 13213 if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 13214 info |= MAY_BE_RC1|MAY_BE_RCN; 13215 } 13216 13217 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL, ZREG_TMP1, ZREG_TMP2 13218 } 13219 } 13220 13221 if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) { 13222 zend_jit_addr var_addr = prop_addr; 13223 uint32_t var_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN; 13224 uint32_t var_def_info = MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN; 13225 13226 if (use_prop_guard) { 13227 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 13228 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 13229 if (!exit_addr) { 13230 return 0; 13231 } 13232 13233 | IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr, ZREG_TMP1 13234 var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)); 13235 } 13236 13237 if (var_info & MAY_BE_REF) { 13238 may_throw = 1; 13239 var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 13240 | LOAD_ZVAL_ADDR REG0, prop_addr 13241 | IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, >2, ZREG_TMP1 13242 | GET_ZVAL_PTR FCARG1x, var_addr, TMP1 13243 | ldr TMP1, [FCARG1x, #offsetof(zend_reference, sources.ptr)] 13244 | cbnz TMP1, >1 13245 | add REG0, FCARG1x, #offsetof(zend_reference, val) 13246 |.cold_code 13247 |1: 13248 if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2 || Z_OFFSET(val_addr) != 0) { 13249 | LOAD_ZVAL_ADDR FCARG2x, val_addr 13250 } 13251 if (opline) { 13252 | SET_EX_OPLINE opline, REG0 13253 } 13254 | LOAD_ADDR CARG3, binary_op 13255 if (((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR)) 13256 && (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13257 | EXT_CALL zend_jit_assign_op_to_typed_ref_tmp, REG0 13258 } else { 13259 | EXT_CALL zend_jit_assign_op_to_typed_ref, REG0 13260 } 13261 | b >9 13262 |.code 13263 |2: 13264 } 13265 13266 switch (opline->extended_value) { 13267 case ZEND_ADD: 13268 case ZEND_SUB: 13269 case ZEND_MUL: 13270 if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || 13271 (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13272 if (opline->extended_value != ZEND_ADD || 13273 (var_info & MAY_BE_ANY) != MAY_BE_ARRAY || 13274 (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) { 13275 may_throw = 1; 13276 } 13277 } 13278 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, 13279 1 /* may overflow */, 0)) { 13280 return 0; 13281 } 13282 break; 13283 case ZEND_BW_OR: 13284 case ZEND_BW_AND: 13285 case ZEND_BW_XOR: 13286 may_throw = 1; 13287 if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || 13288 (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13289 if ((var_info & MAY_BE_ANY) != MAY_BE_STRING || 13290 (val_info & MAY_BE_ANY) != MAY_BE_STRING) { 13291 may_throw = 1; 13292 } 13293 } 13294 goto long_math; 13295 case ZEND_SL: 13296 case ZEND_SR: 13297 if ((var_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || 13298 (val_info & (MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13299 may_throw = 1; 13300 } 13301 if ((opline+1)->op1_type != IS_CONST || 13302 Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG || 13303 Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) < 0) { 13304 may_throw = 1; 13305 } 13306 goto long_math; 13307 case ZEND_MOD: 13308 if ((var_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || 13309 (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13310 if (opline->extended_value != ZEND_ADD || 13311 (var_info & MAY_BE_ANY) != MAY_BE_ARRAY || 13312 (val_info & MAY_BE_ANY) == MAY_BE_ARRAY) { 13313 may_throw = 1; 13314 } 13315 } 13316 if ((opline+1)->op1_type != IS_CONST || 13317 Z_TYPE_P(RT_CONSTANT((opline+1), (opline+1)->op1)) != IS_LONG || 13318 Z_LVAL_P(RT_CONSTANT((opline+1), (opline+1)->op1)) == 0) { 13319 may_throw = 1; 13320 } 13321long_math: 13322 if (!zend_jit_long_math_helper(Dst, opline, opline->extended_value, 13323 IS_CV, opline->op1, var_addr, var_info, NULL, 13324 (opline+1)->op1_type, (opline+1)->op1, val_addr, val_info, 13325 val_range, 13326 0, var_addr, var_def_info, var_info, 0)) { 13327 return 0; 13328 } 13329 break; 13330 case ZEND_CONCAT: 13331 may_throw = 1; 13332 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, 13333 0)) { 13334 return 0; 13335 } 13336 break; 13337 default: 13338 ZEND_UNREACHABLE(); 13339 } 13340 } 13341 13342 if (needs_slow_path) { 13343 may_throw = 1; 13344 |.cold_code 13345 |7: 13346 | SET_EX_OPLINE opline, REG0 13347 | // value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value)); 13348 | LOAD_ADDR FCARG2x, name 13349 | LOAD_ZVAL_ADDR CARG3, val_addr 13350 | ldr CARG4, EX->run_time_cache 13351 | ADD_SUB_64_WITH_CONST_32 add, CARG4, CARG4, (opline+1)->extended_value, TMP1 13352 | LOAD_ADDR CARG5, binary_op 13353 | EXT_CALL zend_jit_assign_obj_op_helper, REG0 13354 13355 if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 13356 val_info |= MAY_BE_RC1|MAY_BE_RCN; 13357 } 13358 13359 |8: 13360 | // FREE_OP_DATA(); 13361 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2 13362 | b >9 13363 |.code 13364 } 13365 13366 |9: 13367 if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { 13368 if ((op1_info & MAY_HAVE_DTOR) && (op1_info & MAY_BE_RC1)) { 13369 may_throw = 1; 13370 } 13371 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 13372 } 13373 13374 if (may_throw) { 13375 if (!zend_jit_check_exception(Dst)) { 13376 return 0; 13377 } 13378 } 13379 13380 return 1; 13381} 13382 13383static int zend_jit_assign_obj(dasm_State **Dst, 13384 const zend_op *opline, 13385 const zend_op_array *op_array, 13386 zend_ssa *ssa, 13387 const zend_ssa_op *ssa_op, 13388 uint32_t op1_info, 13389 zend_jit_addr op1_addr, 13390 uint32_t val_info, 13391 bool op1_indirect, 13392 zend_class_entry *ce, 13393 bool ce_is_instanceof, 13394 bool on_this, 13395 bool delayed_fetch_this, 13396 zend_class_entry *trace_ce, 13397 uint8_t prop_type, 13398 int may_throw) 13399{ 13400 zval *member; 13401 zend_string *name; 13402 zend_property_info *prop_info; 13403 zend_jit_addr val_addr = OP1_DATA_ADDR(); 13404 zend_jit_addr res_addr = 0; 13405 zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); 13406 zend_jit_addr prop_addr; 13407 bool needs_slow_path = 0; 13408 bool needs_val_dtor = 0; 13409 13410 if (RETURN_VALUE_USED(opline)) { 13411 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 13412 } 13413 13414 ZEND_ASSERT(opline->op2_type == IS_CONST); 13415 ZEND_ASSERT(op1_info & MAY_BE_OBJECT); 13416 13417 member = RT_CONSTANT(opline, opline->op2); 13418 ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); 13419 name = Z_STR_P(member); 13420 prop_info = zend_get_known_property_info(op_array, ce, name, on_this, op_array->filename); 13421 13422 if (on_this) { 13423 | GET_ZVAL_PTR FCARG1x, this_addr, TMP1 13424 } else { 13425 if (opline->op1_type == IS_VAR 13426 && (op1_info & MAY_BE_INDIRECT) 13427 && Z_REG(op1_addr) == ZREG_FP) { 13428 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 13429 | IF_NOT_Z_TYPE FCARG1x, IS_INDIRECT, >1, TMP1w 13430 | GET_Z_PTR FCARG1x, FCARG1x 13431 |1: 13432 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 13433 } 13434 if (op1_info & MAY_BE_REF) { 13435 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 13436 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 13437 } 13438 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 13439 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 13440 } 13441 if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) { 13442 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 13443 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 13444 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 13445 13446 if (!exit_addr) { 13447 return 0; 13448 } 13449 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr, ZREG_TMP1 13450 } else { 13451 | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1, ZREG_TMP1 13452 |.cold_code 13453 |1: 13454 | SET_EX_OPLINE opline, REG0 13455 if (Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 13456 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 13457 } 13458 | LOAD_ADDR FCARG2x, ZSTR_VAL(name) 13459 | EXT_CALL zend_jit_invalid_property_assign, REG0 13460 if (RETURN_VALUE_USED(opline)) { 13461 | SET_ZVAL_TYPE_INFO res_addr, IS_NULL, TMP1w, TMP2 13462 } 13463 if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR)) 13464 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13465 needs_val_dtor = 1; 13466 | b >7 13467 } else { 13468 | b >9 13469 } 13470 |.code 13471 } 13472 } 13473 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 13474 } 13475 13476 if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { 13477 prop_info = zend_get_known_property_info(op_array, trace_ce, name, on_this, op_array->filename); 13478 if (prop_info) { 13479 ce = trace_ce; 13480 ce_is_instanceof = 0; 13481 if (!(op1_info & MAY_BE_CLASS_GUARD)) { 13482 if (on_this && JIT_G(current_frame) 13483 && TRACE_FRAME_IS_THIS_CLASS_CHECKED(JIT_G(current_frame))) { 13484 ZEND_ASSERT(JIT_G(current_frame)->ce == ce); 13485 } else if (zend_jit_class_guard(Dst, opline, ce)) { 13486 if (on_this && JIT_G(current_frame)) { 13487 JIT_G(current_frame)->ce = ce; 13488 TRACE_FRAME_SET_THIS_CLASS_CHECKED(JIT_G(current_frame)); 13489 } 13490 } else { 13491 return 0; 13492 } 13493 if (ssa->var_info && ssa_op->op1_use >= 0) { 13494 ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; 13495 ssa->var_info[ssa_op->op1_use].ce = ce; 13496 ssa->var_info[ssa_op->op1_use].is_instanceof = ce_is_instanceof; 13497 } 13498 if (ssa->var_info && ssa_op->op1_def >= 0) { 13499 ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD; 13500 ssa->var_info[ssa_op->op1_def].ce = ce; 13501 ssa->var_info[ssa_op->op1_def].is_instanceof = ce_is_instanceof; 13502 } 13503 } 13504 } 13505 } 13506 13507 if (!prop_info) { 13508 needs_slow_path = 1; 13509 13510 | ldr REG0, EX->run_time_cache 13511 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG2, REG0, opline->extended_value, TMP1 13512 | ldr TMP1, [FCARG1x, #offsetof(zend_object, ce)] 13513 | cmp REG2, TMP1 13514 | bne >5 13515 if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) { 13516 | MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, (opline->extended_value + sizeof(void*) * 2), TMP1 13517 } 13518 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, REG0, (opline->extended_value + sizeof(void*)), TMP1 13519 | tst REG0, REG0 13520 | blt >5 13521 | add TMP2, FCARG1x, REG0 13522 | ldrb TMP1w, [TMP2, #offsetof(zval,u1.v.type)] 13523 | IF_TYPE TMP1w, IS_UNDEF, >5 13524 | mov FCARG1x, TMP2 13525 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 13526 if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) { 13527 | cbnz FCARG2x, >1 13528 |.cold_code 13529 |1: 13530 | // value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC); 13531 | SET_EX_OPLINE opline, REG0 13532 | LOAD_ZVAL_ADDR CARG3, val_addr 13533 if (RETURN_VALUE_USED(opline)) { 13534 | LOAD_ZVAL_ADDR CARG4, res_addr 13535 } else { 13536 | mov CARG4, xzr 13537 } 13538 13539 | EXT_CALL zend_jit_assign_to_typed_prop, REG0 13540 13541 if ((opline+1)->op1_type == IS_CONST) { 13542 | // TODO: ??? 13543 | // if (Z_TYPE_P(value) == orig_type) { 13544 | // CACHE_PTR_EX(cache_slot + 2, NULL); 13545 } 13546 13547 if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR)) 13548 && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { 13549 | b >7 13550 } else { 13551 | b >9 13552 } 13553 |.code 13554 } 13555 } else { 13556 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, prop_info->offset); 13557 if (!ce || ce_is_instanceof || !(ce->ce_flags & ZEND_ACC_IMMUTABLE) || ce->__get || ce->__set || (prop_info->flags & ZEND_ACC_READONLY)) { 13558 // Undefined property with magic __get()/__set() 13559 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 13560 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 13561 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 13562 13563 if (!exit_addr) { 13564 return 0; 13565 } 13566 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1 13567 | IF_TYPE TMP2w, IS_UNDEF, &exit_addr 13568 } else { 13569 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP2w, FCARG1x, (prop_info->offset + offsetof(zval,u1.v.type)), TMP1 13570 | IF_TYPE TMP2w, IS_UNDEF, >5 13571 needs_slow_path = 1; 13572 } 13573 } 13574 if (ZEND_TYPE_IS_SET(prop_info->type)) { 13575 uint32_t info = val_info; 13576 13577 | // value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC); 13578 | SET_EX_OPLINE opline, REG0 13579 if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) { 13580 | LOAD_ADDR FCARG2x, prop_info 13581 } else { 13582 int prop_info_offset = 13583 (((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*)); 13584 13585 | ldr REG0, [FCARG1x, #offsetof(zend_object, ce)] 13586 | ldr REG0, [REG0, #offsetof(zend_class_entry, properties_info_table)] 13587 | MEM_ACCESS_64_WITH_UOFFSET ldr, FCARG2x, REG0, prop_info_offset, TMP1 13588 } 13589 | LOAD_ZVAL_ADDR FCARG1x, prop_addr 13590 | LOAD_ZVAL_ADDR CARG3, val_addr 13591 if (RETURN_VALUE_USED(opline)) { 13592 | LOAD_ZVAL_ADDR CARG4, res_addr 13593 } else { 13594 | mov CARG4, xzr 13595 } 13596 13597 | EXT_CALL zend_jit_assign_to_typed_prop, REG0 13598 13599 if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 13600 info |= MAY_BE_RC1|MAY_BE_RCN; 13601 } 13602 13603 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, NULL, ZREG_TMP1, ZREG_TMP2 13604 } 13605 } 13606 13607 if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) { 13608 // value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES()); 13609 if (opline->result_type == IS_UNUSED) { 13610 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)) { 13611 return 0; 13612 } 13613 } else { 13614 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)) { 13615 return 0; 13616 } 13617 } 13618 } 13619 13620 if (needs_slow_path) { 13621 |.cold_code 13622 |5: 13623 | SET_EX_OPLINE opline, REG0 13624 | // value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value)); 13625 | LOAD_ADDR FCARG2x, name 13626 13627 | LOAD_ZVAL_ADDR CARG3, val_addr 13628 | ldr CARG4, EX->run_time_cache 13629 | ADD_SUB_64_WITH_CONST_32 add, CARG4, CARG4, opline->extended_value, TMP1 13630 if (RETURN_VALUE_USED(opline)) { 13631 | LOAD_ZVAL_ADDR CARG5, res_addr 13632 } else { 13633 | mov CARG5, xzr 13634 } 13635 13636 | EXT_CALL zend_jit_assign_obj_helper, REG0 13637 13638 if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 13639 val_info |= MAY_BE_RC1|MAY_BE_RCN; 13640 } 13641 13642 |7: 13643 | // FREE_OP_DATA(); 13644 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2 13645 | b >9 13646 |.code 13647 } else if (needs_val_dtor) { 13648 |.cold_code 13649 |7: 13650 | // FREE_OP_DATA(); 13651 | FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline, ZREG_TMP1, ZREG_TMP2 13652 | b >9 13653 |.code 13654 } 13655 13656 |9: 13657 if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { 13658 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2 13659 } 13660 13661 if (may_throw) { 13662 if (!zend_jit_check_exception(Dst)) { 13663 return 0; 13664 } 13665 } 13666 13667 return 1; 13668} 13669 13670static int zend_jit_free(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, int may_throw) 13671{ 13672 zend_jit_addr op1_addr = OP1_ADDR(); 13673 13674 if (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { 13675 if (may_throw) { 13676 | SET_EX_OPLINE opline, REG0 13677 } 13678 if (opline->opcode == ZEND_FE_FREE && (op1_info & (MAY_BE_OBJECT|MAY_BE_REF))) { 13679 if (op1_info & MAY_BE_ARRAY) { 13680 | IF_ZVAL_TYPE op1_addr, IS_ARRAY, >7, ZREG_TMP1 13681 } 13682 | MEM_ACCESS_32_WITH_UOFFSET ldr, FCARG1w, FP, (opline->op1.var + offsetof(zval, u2.fe_iter_idx)), TMP1 13683 | mvn TMP1w, wzr // TODO: DynAsm fails loading #-1 13684 | cmp FCARG1w, TMP1w 13685 | beq >7 13686 | EXT_CALL zend_hash_iterator_del, REG0 13687 |7: 13688 } 13689 | ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline, ZREG_TMP1, ZREG_TMP2 13690 if (may_throw) { 13691 if (!zend_jit_check_exception(Dst)) { 13692 return 0; 13693 } 13694 } 13695 } 13696 return 1; 13697} 13698 13699static int zend_jit_echo(dasm_State **Dst, const zend_op *opline, uint32_t op1_info) 13700{ 13701 if (opline->op1_type == IS_CONST) { 13702 zval *zv; 13703 size_t len; 13704 13705 zv = RT_CONSTANT(opline, opline->op1); 13706 ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); 13707 len = Z_STRLEN_P(zv); 13708 13709 if (len > 0) { 13710 const char *str = Z_STRVAL_P(zv); 13711 13712 | SET_EX_OPLINE opline, REG0 13713 | LOAD_ADDR CARG1, str 13714 | LOAD_64BIT_VAL CARG2, len 13715 | EXT_CALL zend_write, REG0 13716 if (!zend_jit_check_exception(Dst)) { 13717 return 0; 13718 } 13719 } 13720 } else { 13721 zend_jit_addr op1_addr = OP1_ADDR(); 13722 13723 ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING); 13724 13725 | SET_EX_OPLINE opline, REG0 13726 | GET_ZVAL_PTR REG0, op1_addr, TMP1 13727 | add CARG1, REG0, #offsetof(zend_string, val) 13728 | ldr CARG2, [REG0, #offsetof(zend_string, len)] 13729 | EXT_CALL zend_write, REG0 13730 if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { 13731 | ZVAL_PTR_DTOR op1_addr, op1_info, 0, 0, opline, ZREG_TMP1, ZREG_TMP2 13732 } 13733 if (!zend_jit_check_exception(Dst)) { 13734 return 0; 13735 } 13736 } 13737 return 1; 13738} 13739 13740static 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) 13741{ 13742 if (opline->op1_type == IS_CONST) { 13743 zval *zv; 13744 size_t len; 13745 13746 zv = RT_CONSTANT(opline, opline->op1); 13747 ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); 13748 len = Z_STRLEN_P(zv); 13749 13750 | SET_ZVAL_LVAL res_addr, len, TMP1, TMP2 13751 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 13752 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 13753 } else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) { 13754 return 0; 13755 } 13756 } else { 13757 ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING); 13758 13759 if (Z_MODE(res_addr) == IS_REG) { 13760 | GET_ZVAL_PTR Rx(Z_REG(res_addr)), op1_addr, TMP1 13761 | ldr Rx(Z_REG(res_addr)), [Rx(Z_REG(res_addr)), #offsetof(zend_string, len)] 13762 if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) { 13763 return 0; 13764 } 13765 } else { 13766 | GET_ZVAL_PTR REG0, op1_addr, TMP1 13767 | ldr REG0, [REG0, #offsetof(zend_string, len)] 13768 | SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1 13769 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 13770 } 13771 | FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2 13772 } 13773 return 1; 13774} 13775 13776static 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) 13777{ 13778 if (opline->op1_type == IS_CONST) { 13779 zval *zv; 13780 zend_long count; 13781 13782 zv = RT_CONSTANT(opline, opline->op1); 13783 ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); 13784 count = zend_hash_num_elements(Z_ARRVAL_P(zv)); 13785 13786 | SET_ZVAL_LVAL res_addr, count, TMP1, TMP2 13787 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 13788 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 13789 } else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) { 13790 return 0; 13791 } 13792 } else { 13793 ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY); 13794 // Note: See the implementation of ZEND_COUNT in Zend/zend_vm_def.h - arrays do not contain IS_UNDEF starting in php 8.1+. 13795 13796 if (Z_MODE(res_addr) == IS_REG) { 13797 | GET_ZVAL_PTR Rx(Z_REG(res_addr)), op1_addr, TMP1 13798 // Sign-extend the 32-bit value to a potentially 64-bit zend_long 13799 | ldr Rw(Z_REG(res_addr)), [Rx(Z_REG(res_addr)), #offsetof(HashTable, nNumOfElements)] 13800 if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, MAY_BE_LONG)) { 13801 return 0; 13802 } 13803 } else { 13804 | GET_ZVAL_PTR REG0, op1_addr, TMP1 13805 // Sign-extend the 32-bit value to a potentially 64-bit zend_long 13806 | ldr REG0w, [REG0, #offsetof(HashTable, nNumOfElements)] 13807 | SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1 13808 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 13809 } 13810 | FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline, ZREG_TMP1, ZREG_TMP2 13811 } 13812 13813 if (may_throw) { 13814 return zend_jit_check_exception(Dst); 13815 } 13816 return 1; 13817} 13818 13819static int zend_jit_load_this(dasm_State **Dst, uint32_t var) 13820{ 13821 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); 13822 13823 | ldr FCARG1x, EX->This.value.ptr 13824 | SET_ZVAL_PTR var_addr, FCARG1x, TMP1 13825 | SET_ZVAL_TYPE_INFO var_addr, IS_OBJECT_EX, TMP1w, TMP2 13826 | GC_ADDREF FCARG1x, TMP1w 13827 return 1; 13828} 13829 13830static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool check_only) 13831{ 13832 if (!op_array->scope || 13833 (op_array->fn_flags & ZEND_ACC_STATIC) || 13834 ((op_array->fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_IMMUTABLE)) == ZEND_ACC_CLOSURE)) { 13835 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 13836 if (!JIT_G(current_frame) || 13837 !TRACE_FRAME_IS_THIS_CHECKED(JIT_G(current_frame))) { 13838 13839 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); 13840 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 13841 13842 if (!exit_addr) { 13843 return 0; 13844 } 13845 13846 | ldrb TMP1w, EX->This.u1.v.type 13847 | cmp TMP1w, #IS_OBJECT 13848 | bne &exit_addr 13849 13850 if (JIT_G(current_frame)) { 13851 TRACE_FRAME_SET_THIS_CHECKED(JIT_G(current_frame)); 13852 } 13853 } 13854 } else { 13855 13856 | ldrb TMP1w, EX->This.u1.v.type 13857 | cmp TMP1w, #IS_OBJECT 13858 | bne >1 13859 |.cold_code 13860 |1: 13861 | SET_EX_OPLINE opline, REG0 13862 | b ->invalid_this 13863 |.code 13864 } 13865 } 13866 13867 if (!check_only) { 13868 if (!zend_jit_load_this(Dst, opline->result.var)) { 13869 return 0; 13870 } 13871 } 13872 return 1; 13873} 13874 13875static 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) 13876{ 13877 uint32_t count; 13878 Bucket *p; 13879 const zend_op *target; 13880 int b; 13881 int32_t exit_point; 13882 const void *exit_addr; 13883 13884 if (default_label) { 13885 | cbz REG0, &default_label 13886 } else if (next_opline) { 13887 | cbz REG0, >3 13888 } else { 13889 | cbz REG0, =>default_b 13890 } 13891 | LOAD_ADDR FCARG1x, jumptable 13892 | ldr TMP1, [FCARG1x, #offsetof(HashTable, arData)] 13893 | sub REG0, REG0, TMP1 13894 if (HT_IS_PACKED(jumptable)) { 13895 | mov FCARG1x, #(sizeof(zval) / sizeof(void*)) 13896 } else { 13897 | mov FCARG1x, #(sizeof(Bucket) / sizeof(void*)) 13898 } 13899 | sdiv REG0, REG0, FCARG1x 13900 | adr FCARG1x, >4 13901 | ldr TMP1, [FCARG1x, REG0] 13902 | br TMP1 13903 13904 |.jmp_table 13905 |.align 8 13906 |4: 13907 if (trace_info) { 13908 trace_info->jmp_table_size += zend_hash_num_elements(jumptable); 13909 } 13910 13911 count = jumptable->nNumUsed; 13912 p = jumptable->arData; 13913 do { 13914 if (Z_TYPE(p->val) == IS_UNDEF) { 13915 if (default_label) { 13916 | .addr &default_label 13917 } else if (next_opline) { 13918 | .addr >3 13919 } else { 13920 | .addr =>default_b 13921 } 13922 } else { 13923 target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val)); 13924 if (!next_opline) { 13925 b = ssa->cfg.map[target - op_array->opcodes]; 13926 | .addr =>b 13927 } else if (next_opline == target) { 13928 | .addr >3 13929 } else { 13930 exit_point = zend_jit_trace_get_exit_point(target, 0); 13931 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 13932 if (!exit_addr) { 13933 return 0; 13934 } 13935 | .addr &exit_addr 13936 } 13937 } 13938 if (HT_IS_PACKED(jumptable)) { 13939 p = (Bucket*)(((zval*)p)+1); 13940 } else { 13941 p++; 13942 } 13943 count--; 13944 } while (count); 13945 |.code 13946 13947 return 1; 13948} 13949 13950static 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) 13951{ 13952 HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2)); 13953 const zend_op *next_opline = NULL; 13954 13955 if (trace) { 13956 ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END); 13957 ZEND_ASSERT(trace->opline != NULL); 13958 next_opline = trace->opline; 13959 } 13960 13961 if (opline->op1_type == IS_CONST) { 13962 zval *zv = RT_CONSTANT(opline, opline->op1); 13963 zval *jump_zv = NULL; 13964 int b; 13965 13966 if (opline->opcode == ZEND_SWITCH_LONG) { 13967 if (Z_TYPE_P(zv) == IS_LONG) { 13968 jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv)); 13969 } 13970 } else if (opline->opcode == ZEND_SWITCH_STRING) { 13971 if (Z_TYPE_P(zv) == IS_STRING) { 13972 jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv)); 13973 } 13974 } else if (opline->opcode == ZEND_MATCH) { 13975 if (Z_TYPE_P(zv) == IS_LONG) { 13976 jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv)); 13977 } else if (Z_TYPE_P(zv) == IS_STRING) { 13978 jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv)); 13979 } 13980 } else { 13981 ZEND_UNREACHABLE(); 13982 } 13983 if (next_opline) { 13984 const zend_op *target; 13985 13986 if (jump_zv != NULL) { 13987 target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)); 13988 } else { 13989 target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); 13990 } 13991 ZEND_ASSERT(target == next_opline); 13992 } else { 13993 if (jump_zv != NULL) { 13994 b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes]; 13995 } else { 13996 b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes]; 13997 } 13998 | b =>b 13999 } 14000 } else { 14001 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; 14002 uint32_t op1_info = OP1_INFO(); 14003 zend_jit_addr op1_addr = OP1_ADDR(); 14004 const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); 14005 const zend_op *target; 14006 int default_b = next_opline ? -1 : ssa->cfg.map[default_opline - op_array->opcodes]; 14007 int b; 14008 int32_t exit_point; 14009 const void *fallback_label = NULL; 14010 const void *default_label = NULL; 14011 const void *exit_addr; 14012 14013 if (next_opline) { 14014 if (next_opline != opline + 1) { 14015 exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); 14016 fallback_label = zend_jit_trace_get_exit_addr(exit_point); 14017 if (!fallback_label) { 14018 return 0; 14019 } 14020 } 14021 if (next_opline != default_opline) { 14022 exit_point = zend_jit_trace_get_exit_point(default_opline, 0); 14023 default_label = zend_jit_trace_get_exit_addr(exit_point); 14024 if (!default_label) { 14025 return 0; 14026 } 14027 } 14028 } 14029 14030 if (opline->opcode == ZEND_SWITCH_LONG) { 14031 if (op1_info & MAY_BE_LONG) { 14032 if (op1_info & MAY_BE_REF) { 14033 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >1, ZREG_TMP1 14034 | GET_ZVAL_LVAL ZREG_FCARG2, op1_addr, TMP1 14035 |.cold_code 14036 |1: 14037 | // ZVAL_DEREF(op) 14038 if (fallback_label) { 14039 | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label, ZREG_TMP1 14040 } else { 14041 | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3, ZREG_TMP1 14042 } 14043 | GET_ZVAL_PTR FCARG2x, op1_addr, TMP1 14044 if (fallback_label) { 14045 | add TMP1, FCARG2x, #offsetof(zend_reference, val) 14046 | IF_NOT_Z_TYPE TMP1, IS_LONG, &fallback_label, TMP2w 14047 } else { 14048 | add TMP1, FCARG2x, #offsetof(zend_reference, val) 14049 | IF_NOT_Z_TYPE TMP1, IS_LONG, >3, TMP2w 14050 } 14051 | ldr FCARG2x, [FCARG2x, #offsetof(zend_reference, val.value.lval)] 14052 | b >2 14053 |.code 14054 |2: 14055 } else { 14056 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) { 14057 if (fallback_label) { 14058 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &fallback_label, ZREG_TMP1 14059 } else { 14060 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1 14061 } 14062 } 14063 | GET_ZVAL_LVAL ZREG_FCARG2, op1_addr, TMP1 14064 } 14065 if (HT_IS_PACKED(jumptable)) { 14066 uint32_t count = jumptable->nNumUsed; 14067 zval *zv = jumptable->arPacked; 14068 14069 | CMP_64_WITH_CONST_32 FCARG2x, jumptable->nNumUsed, TMP1 14070 if (default_label) { 14071 | bhs &default_label 14072 } else if (next_opline) { 14073 | bhs >3 14074 } else { 14075 | bhs =>default_b 14076 } 14077 | adr REG0, >4 14078 | ldr TMP1, [REG0, FCARG2x, lsl #3] 14079 | br TMP1 14080 14081 |.jmp_table 14082 |.align 8 14083 |4: 14084 if (trace_info) { 14085 trace_info->jmp_table_size += count; 14086 } 14087 do { 14088 if (Z_TYPE_P(zv) == IS_UNDEF) { 14089 if (default_label) { 14090 | .addr &default_label 14091 } else if (next_opline) { 14092 | .addr >3 14093 } else { 14094 | .addr =>default_b 14095 } 14096 } else { 14097 target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(zv)); 14098 if (!next_opline) { 14099 b = ssa->cfg.map[target - op_array->opcodes]; 14100 | .addr =>b 14101 } else if (next_opline == target) { 14102 | .addr >3 14103 } else { 14104 exit_point = zend_jit_trace_get_exit_point(target, 0); 14105 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 14106 if (!exit_addr) { 14107 return 0; 14108 } 14109 | .addr &exit_addr 14110 } 14111 } 14112 zv++; 14113 count--; 14114 } while (count); 14115 |.code 14116 |3: 14117 } else { 14118 | LOAD_ADDR FCARG1x, jumptable 14119 | EXT_CALL zend_hash_index_find, REG0 14120 | mov REG0, RETVALx 14121 if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) { 14122 return 0; 14123 } 14124 |3: 14125 } 14126 } 14127 } else if (opline->opcode == ZEND_SWITCH_STRING) { 14128 if (op1_info & MAY_BE_STRING) { 14129 if (op1_info & MAY_BE_REF) { 14130 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >1, ZREG_TMP1 14131 | GET_ZVAL_PTR FCARG2x, op1_addr, TMP1 14132 |.cold_code 14133 |1: 14134 | // ZVAL_DEREF(op) 14135 if (fallback_label) { 14136 | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label, ZREG_TMP1 14137 } else { 14138 | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3, ZREG_TMP1 14139 } 14140 | GET_ZVAL_PTR FCARG2x, op1_addr, TMP1 14141 if (fallback_label) { 14142 | add TMP1, FCARG2x, #offsetof(zend_reference, val) 14143 | IF_NOT_Z_TYPE TMP1, IS_STRING, &fallback_label, TMP2w 14144 } else { 14145 | add TMP1, FCARG2x, #offsetof(zend_reference, val) 14146 | IF_NOT_Z_TYPE TMP1, IS_STRING, >3, TMP2w 14147 } 14148 | ldr FCARG2x, [FCARG2x, #offsetof(zend_reference, val.value.ptr)] 14149 | b >2 14150 |.code 14151 |2: 14152 } else { 14153 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_STRING)) { 14154 if (fallback_label) { 14155 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &fallback_label, ZREG_TMP1 14156 } else { 14157 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3, ZREG_TMP1 14158 } 14159 } 14160 | GET_ZVAL_PTR FCARG2x, op1_addr, TMP1 14161 } 14162 | LOAD_ADDR FCARG1x, jumptable 14163 | EXT_CALL zend_hash_find, REG0 14164 | mov REG0, RETVALx 14165 if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) { 14166 return 0; 14167 } 14168 |3: 14169 } 14170 } else if (opline->opcode == ZEND_MATCH) { 14171 if (op1_info & (MAY_BE_LONG|MAY_BE_STRING)) { 14172 if (op1_info & MAY_BE_REF) { 14173 | LOAD_ZVAL_ADDR FCARG2x, op1_addr 14174 | ZVAL_DEREF FCARG2x, op1_info, TMP1w 14175 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0); 14176 } 14177 | LOAD_ADDR FCARG1x, jumptable 14178 if (op1_info & MAY_BE_LONG) { 14179 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) { 14180 if (op1_info & MAY_BE_STRING) { 14181 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >5, ZREG_TMP1 14182 } else if (op1_info & MAY_BE_UNDEF) { 14183 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6, ZREG_TMP1 14184 } else if (default_label) { 14185 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &default_label, ZREG_TMP1 14186 } else if (next_opline) { 14187 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3, ZREG_TMP1 14188 } else { 14189 | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, =>default_b, ZREG_TMP1 14190 } 14191 } 14192 | GET_ZVAL_LVAL ZREG_FCARG2, op1_addr, TMP1 14193 | EXT_CALL zend_hash_index_find, REG0 14194 | mov REG0, RETVALx 14195 if (op1_info & MAY_BE_STRING) { 14196 | b >2 14197 } 14198 } 14199 if (op1_info & MAY_BE_STRING) { 14200 |5: 14201 if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_STRING))) { 14202 if (op1_info & MAY_BE_UNDEF) { 14203 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6, ZREG_TMP1 14204 } else if (default_label) { 14205 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &default_label, ZREG_TMP1 14206 } else if (next_opline) { 14207 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3, ZREG_TMP1 14208 } else { 14209 | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, =>default_b, ZREG_TMP1 14210 } 14211 } 14212 | GET_ZVAL_PTR FCARG2x, op1_addr, TMP1 14213 | EXT_CALL zend_hash_find, REG0 14214 | mov REG0, RETVALx 14215 } 14216 |2: 14217 if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) { 14218 return 0; 14219 } 14220 } 14221 if (op1_info & MAY_BE_UNDEF) { 14222 |6: 14223 if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_STRING))) { 14224 if (default_label) { 14225 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, &default_label, ZREG_TMP1 14226 } else if (next_opline) { 14227 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >3, ZREG_TMP1 14228 } else { 14229 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, =>default_b, ZREG_TMP1 14230 } 14231 } 14232 | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); 14233 | SET_EX_OPLINE opline, REG0 14234 | LOAD_32BIT_VAL FCARG1w, opline->op1.var 14235 | EXT_CALL zend_jit_undefined_op_helper, REG0 14236 if (!zend_jit_check_exception_undef_result(Dst, opline)) { 14237 return 0; 14238 } 14239 } 14240 if (default_label) { 14241 | b &default_label 14242 } else if (next_opline) { 14243 | b >3 14244 } else { 14245 | b =>default_b 14246 } 14247 |3: 14248 } else { 14249 ZEND_UNREACHABLE(); 14250 } 14251 } 14252 return 1; 14253} 14254 14255static bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info) 14256{ 14257 zend_arg_info *arg_info = &op_array->arg_info[-1]; 14258 ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type)); 14259 zend_jit_addr op1_addr = OP1_ADDR(); 14260 bool needs_slow_check = 1; 14261 bool slow_check_in_cold = 1; 14262 uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY; 14263 14264 if (type_mask == 0) { 14265 slow_check_in_cold = 0; 14266 } else { 14267 if (((op1_info & MAY_BE_ANY) & type_mask) == 0) { 14268 slow_check_in_cold = 0; 14269 } else if (((op1_info & MAY_BE_ANY) | type_mask) == type_mask) { 14270 needs_slow_check = 0; 14271 } else if (is_power_of_two(type_mask)) { 14272 uint32_t type_code = concrete_type(type_mask); 14273 | IF_NOT_ZVAL_TYPE op1_addr, type_code, >6, ZREG_TMP1 14274 } else { 14275 | mov REG2w, #1 14276 | GET_ZVAL_TYPE REG1w, op1_addr, TMP1 14277 | lsl REG2w, REG2w, REG1w 14278 | TST_32_WITH_CONST REG2w, type_mask, TMP1w 14279 | beq >6 14280 } 14281 } 14282 if (needs_slow_check) { 14283 if (slow_check_in_cold) { 14284 |.cold_code 14285 |6: 14286 } 14287 | SET_EX_OPLINE opline, REG1 14288 if (op1_info & MAY_BE_UNDEF) { 14289 | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >7, ZREG_TMP1 14290 | LOAD_32BIT_VAL FCARG1x, opline->op1.var 14291 | EXT_CALL zend_jit_undefined_op_helper, REG0 14292 | cbz RETVALx, ->exception_handler 14293 | LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval 14294 | b >8 14295 } 14296 |7: 14297 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 14298 |8: 14299 | ldr FCARG2x, EX->func 14300 | LOAD_ADDR CARG3, (ptrdiff_t)arg_info 14301 | ldr REG0, EX->run_time_cache 14302 | ADD_SUB_64_WITH_CONST_32 add, CARG4, REG0, opline->op2.num, TMP1 14303 | EXT_CALL zend_jit_verify_return_slow, REG0 14304 if (!zend_jit_check_exception(Dst)) { 14305 return 0; 14306 } 14307 if (slow_check_in_cold) { 14308 | b >9 14309 |.code 14310 } 14311 } 14312 |9: 14313 return 1; 14314} 14315 14316static 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) 14317{ 14318 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 14319 14320 // TODO: support for empty() ??? 14321 ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY)); 14322 14323 if (op1_info & MAY_BE_REF) { 14324 if (Z_MODE(op1_addr) != IS_MEM_ZVAL || Z_REG(op1_addr) != ZREG_FCARG1 || Z_OFFSET(op1_addr) != 0) { 14325 | LOAD_ZVAL_ADDR FCARG1x, op1_addr 14326 op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 14327 } 14328 | ZVAL_DEREF FCARG1x, op1_info, TMP1w 14329 |1: 14330 } 14331 14332 if (!(op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) { 14333 if (exit_addr) { 14334 ZEND_ASSERT(smart_branch_opcode == ZEND_JMPZ); 14335 } else if (smart_branch_opcode) { 14336 if (smart_branch_opcode == ZEND_JMPNZ) { 14337 | b =>target_label 14338 } 14339 } else { 14340 | SET_ZVAL_TYPE_INFO res_addr, IS_TRUE, TMP1w, TMP2 14341 } 14342 } else if (!(op1_info & (MAY_BE_ANY - MAY_BE_NULL))) { 14343 if (exit_addr) { 14344 ZEND_ASSERT(smart_branch_opcode == ZEND_JMPNZ); 14345 } else if (smart_branch_opcode) { 14346 if (smart_branch_opcode != ZEND_JMPNZ) { 14347 | b =>target_label 14348 } 14349 } else { 14350 | SET_ZVAL_TYPE_INFO res_addr, IS_FALSE, TMP1w, TMP2 14351 } 14352 } else { 14353 ZEND_ASSERT(Z_MODE(op1_addr) == IS_MEM_ZVAL); 14354 | MEM_ACCESS_8_WITH_UOFFSET ldrb, TMP1w, Rx(Z_REG(op1_addr)), (Z_OFFSET(op1_addr)+offsetof(zval, u1.v.type)), TMP1 14355 | cmp TMP1w, #IS_NULL 14356 if (exit_addr) { 14357 if (smart_branch_opcode == ZEND_JMPNZ) { 14358 | bgt &exit_addr 14359 } else { 14360 | ble &exit_addr 14361 } 14362 } else if (smart_branch_opcode) { 14363 if (smart_branch_opcode == ZEND_JMPZ) { 14364 | ble =>target_label 14365 } else if (smart_branch_opcode == ZEND_JMPNZ) { 14366 | bgt =>target_label 14367 } else { 14368 ZEND_UNREACHABLE(); 14369 } 14370 } else { 14371 | cset REG0w, gt 14372 | add REG0w, REG0w, #IS_FALSE 14373 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 14374 } 14375 } 14376 14377 return 1; 14378} 14379 14380static int zend_jit_fe_reset(dasm_State **Dst, const zend_op *opline, uint32_t op1_info) 14381{ 14382 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 14383 14384 if (opline->op1_type == IS_CONST) { 14385 zval *zv = RT_CONSTANT(opline, opline->op1); 14386 14387 | ZVAL_COPY_CONST res_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0 14388 if (Z_REFCOUNTED_P(zv)) { 14389 | ADDREF_CONST zv, REG0, TMP1 14390 } 14391 } else { 14392 zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); 14393 14394 | // ZVAL_COPY(res, value); 14395 | ZVAL_COPY_VALUE res_addr, -1, op1_addr, op1_info, ZREG_REG0, ZREG_FCARG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 14396 if (opline->op1_type == IS_CV) { 14397 | TRY_ADDREF op1_info, REG0w, FCARG1x, TMP1w 14398 } 14399 } 14400 | // Z_FE_POS_P(res) = 0; 14401 | MEM_ACCESS_32_WITH_UOFFSET str, wzr, FP, (opline->result.var + offsetof(zval, u2.fe_pos)), TMP1 14402 14403 return 1; 14404} 14405 14406static 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) 14407{ 14408 zend_jit_addr op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); 14409 14410 if (!MAY_BE_HASH(op1_info) && !MAY_BE_PACKED(op1_info)) { 14411 /* empty array */ 14412 if (exit_addr) { 14413 if (exit_opcode == ZEND_JMP) { 14414 | b &exit_addr 14415 } 14416 } else { 14417 | b =>target_label 14418 } 14419 return 1; 14420 } 14421 14422 | // array = EX_VAR(opline->op1.var); 14423 | // fe_ht = Z_ARRVAL_P(array); 14424 | GET_ZVAL_PTR FCARG1x, op1_addr, TMP1 14425 14426 if (op1_info & MAY_BE_PACKED_GUARD) { 14427 int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD); 14428 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 14429 14430 if (!exit_addr) { 14431 return 0; 14432 } 14433 if (op1_info & MAY_BE_ARRAY_PACKED) { 14434 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 14435 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 14436 | beq &exit_addr 14437 } else { 14438 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 14439 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 14440 | bne &exit_addr 14441 } 14442 } 14443 14444 | // pos = Z_FE_POS_P(array); 14445 | MEM_ACCESS_32_WITH_UOFFSET ldr, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1 14446 14447 if (MAY_BE_HASH(op1_info)) { 14448 if (MAY_BE_PACKED(op1_info)) { 14449 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, u.flags)] 14450 | TST_32_WITH_CONST TMP1w, HASH_FLAG_PACKED, TMP2w 14451 | bne >2 14452 } 14453 | // p = fe_ht->arData + pos; 14454 || ZEND_ASSERT(sizeof(Bucket) == 32); 14455 | mov FCARG2w, REG0w 14456 | ldr TMP1, [FCARG1x, #offsetof(zend_array, arData)] 14457 | add FCARG2x, TMP1, FCARG2x, lsl #5 14458 |1: 14459 | // if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { 14460 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, nNumUsed)] 14461 | cmp TMP1w, REG0w 14462 | // ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); 14463 | // ZEND_VM_CONTINUE(); 14464 if (exit_addr) { 14465 if (exit_opcode == ZEND_JMP) { 14466 | bls &exit_addr 14467 } else { 14468 | bls >3 14469 } 14470 } else { 14471 | bls =>target_label 14472 } 14473 | // pos++; 14474 | add REG0w, REG0w, #1 14475 | // value_type = Z_TYPE_INFO_P(value); 14476 | // if (EXPECTED(value_type != IS_UNDEF)) { 14477 if (!exit_addr || exit_opcode == ZEND_JMP) { 14478 | IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, >3, TMP1w 14479 } else { 14480 | IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, &exit_addr, TMP1w 14481 } 14482 | // p++; 14483 | add FCARG2x, FCARG2x, #sizeof(Bucket) 14484 | b <1 14485 if (MAY_BE_PACKED(op1_info)) { 14486 |2: 14487 } 14488 } 14489 if (MAY_BE_PACKED(op1_info)) { 14490 | // p = fe_ht->arPacked + pos; 14491 || ZEND_ASSERT(sizeof(zval) == 16); 14492 | mov FCARG2w, REG0w 14493 | ldr TMP1, [FCARG1x, #offsetof(zend_array, arPacked)] 14494 | add FCARG2x, TMP1, FCARG2x, lsl #4 14495 |1: 14496 | // if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { 14497 | ldr TMP1w, [FCARG1x, #offsetof(zend_array, nNumUsed)] 14498 | cmp TMP1w, REG0w 14499 | // ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value); 14500 | // ZEND_VM_CONTINUE(); 14501 if (exit_addr) { 14502 if (exit_opcode == ZEND_JMP) { 14503 | bls &exit_addr 14504 } else { 14505 | bls >4 14506 } 14507 } else { 14508 | bls =>target_label 14509 } 14510 | // pos++; 14511 | add REG0w, REG0w, #1 14512 | // value_type = Z_TYPE_INFO_P(value); 14513 | // if (EXPECTED(value_type != IS_UNDEF)) { 14514 if (!exit_addr || exit_opcode == ZEND_JMP) { 14515 | IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, >4, TMP1w 14516 } else { 14517 | IF_NOT_Z_TYPE FCARG2x, IS_UNDEF, &exit_addr, TMP1w 14518 } 14519 | // p++; 14520 | add FCARG2x, FCARG2x, #sizeof(zval) 14521 | b <1 14522 } 14523 14524 if (!exit_addr || exit_opcode == ZEND_JMP) { 14525 zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2, 0); 14526 zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var); 14527 uint32_t val_info; 14528 14529 if (RETURN_VALUE_USED(opline)) { 14530 zend_jit_addr res_addr = RES_ADDR(); 14531 14532 if (MAY_BE_HASH(op1_info)) { 14533 |3: 14534 | // Z_FE_POS_P(array) = pos + 1; 14535 | MEM_ACCESS_32_WITH_UOFFSET str, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1 14536 14537 if ((op1_info & MAY_BE_ARRAY_KEY_LONG) 14538 && (op1_info & MAY_BE_ARRAY_KEY_STRING)) { 14539 | // if (!p->key) { 14540 | ldr REG0, [FCARG2x, #offsetof(Bucket, key)] 14541 | cbz REG0, >2 14542 } 14543 if (op1_info & MAY_BE_ARRAY_KEY_STRING) { 14544 | // ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); 14545 | ldr REG0, [FCARG2x, #offsetof(Bucket, key)] 14546 | SET_ZVAL_PTR res_addr, REG0, TMP1 14547 | ldr TMP1w, [REG0, #offsetof(zend_refcounted, gc.u.type_info)] 14548 | TST_32_WITH_CONST TMP1w, IS_STR_INTERNED, TMP2w 14549 | beq >1 14550 | SET_ZVAL_TYPE_INFO res_addr, IS_STRING, TMP1w, TMP2 14551 | b >3 14552 |1: 14553 | GC_ADDREF REG0, TMP1w 14554 | SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX, TMP1w, TMP2 14555 14556 if ((op1_info & MAY_BE_ARRAY_KEY_LONG) || MAY_BE_PACKED(op1_info)) { 14557 | b >3 14558 |2: 14559 } 14560 } 14561 if (op1_info & MAY_BE_ARRAY_KEY_LONG) { 14562 | // ZVAL_LONG(EX_VAR(opline->result.var), p->h); 14563 | ldr REG0, [FCARG2x, #offsetof(Bucket, h)] 14564 | SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1 14565 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 14566 if (MAY_BE_PACKED(op1_info)) { 14567 | b >3 14568 } 14569 } 14570 } 14571 if (MAY_BE_PACKED(op1_info)) { 14572 |4: 14573 | // Z_FE_POS_P(array) = pos + 1; 14574 | MEM_ACCESS_32_WITH_UOFFSET str, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1 14575 | sub REG0w, REG0w, #1 14576 | SET_ZVAL_LVAL_FROM_REG res_addr, REG0, TMP1 14577 | SET_ZVAL_TYPE_INFO res_addr, IS_LONG, TMP1w, TMP2 14578 } 14579 |3: 14580 } else { 14581 |3: 14582 |4: 14583 | // Z_FE_POS_P(array) = pos + 1; 14584 | MEM_ACCESS_32_WITH_UOFFSET str, REG0w, FP, (opline->op1.var + offsetof(zval, u2.fe_pos)), TMP1 14585 } 14586 14587 val_info = ((op1_info & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT); 14588 if (val_info & MAY_BE_ARRAY) { 14589 val_info |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; 14590 } 14591 if (op1_info & MAY_BE_ARRAY_OF_REF) { 14592 val_info |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | 14593 MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; 14594 } else if (val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { 14595 val_info |= MAY_BE_RC1 | MAY_BE_RCN; 14596 } 14597 14598 if (opline->op2_type == IS_CV) { 14599 | // zend_assign_to_variable(variable_ptr, value, IS_CV, EX_USES_STRICT_TYPES()); 14600 if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, op2_info, -1, IS_CV, val_addr, val_info, 0, 1)) { 14601 return 0; 14602 } 14603 } else { 14604 | // ZVAL_COPY(res, value); 14605 | ZVAL_COPY_VALUE var_addr, -1, val_addr, val_info, ZREG_REG0, ZREG_FCARG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 14606 | TRY_ADDREF val_info, REG0w, FCARG1x, TMP1w 14607 } 14608 } else { 14609 |3: 14610 |4: 14611 } 14612 14613 return 1; 14614} 14615 14616static int zend_jit_fetch_constant(dasm_State **Dst, 14617 const zend_op *opline, 14618 const zend_op_array *op_array, 14619 zend_ssa *ssa, 14620 const zend_ssa_op *ssa_op, 14621 zend_jit_addr res_addr) 14622{ 14623 zval *zv = RT_CONSTANT(opline, opline->op2) + 1; 14624 zend_jit_addr const_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); 14625 uint32_t res_info = RES_INFO(); 14626 14627 | // c = CACHED_PTR(opline->extended_value); 14628 | ldr FCARG1x, EX->run_time_cache 14629 | MEM_ACCESS_64_WITH_UOFFSET ldr, REG0, FCARG1x, opline->extended_value, TMP1 14630 | // if (c != NULL) 14631 | cbz REG0, >9 14632 if (!zend_jit_is_persistent_constant(zv, opline->op1.num)) { 14633 | // if (!IS_SPECIAL_CACHE_VAL(c)) 14634 || ZEND_ASSERT(CACHE_SPECIAL == 1); 14635 | TST_64_WITH_ONE REG0 14636 | bne >9 14637 } 14638 |8: 14639 14640 if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) { 14641 zend_jit_trace_stack *stack = JIT_G(current_frame)->stack; 14642 uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); 14643 int32_t exit_point; 14644 const void *exit_addr = NULL; 14645 14646 SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1); 14647 SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_GPR0); 14648 exit_point = zend_jit_trace_get_exit_point(opline+1, 0); 14649 SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info); 14650 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 14651 if (!exit_addr) { 14652 return 0; 14653 } 14654 res_info &= ~MAY_BE_GUARD; 14655 ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; 14656 14657 uint32_t type = concrete_type(res_info); 14658 14659 if (type < IS_STRING) { 14660 | IF_NOT_ZVAL_TYPE const_addr, type, &exit_addr, ZREG_TMP1 14661 } else { 14662 | GET_ZVAL_TYPE_INFO REG2w, const_addr, TMP1 14663 | IF_NOT_TYPE REG2w, type, &exit_addr 14664 } 14665 | ZVAL_COPY_VALUE_V res_addr, -1, const_addr, res_info, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_FPR0 14666 if (type < IS_STRING) { 14667 if (Z_MODE(res_addr) == IS_MEM_ZVAL) { 14668 | SET_ZVAL_TYPE_INFO res_addr, type, TMP1w, TMP2 14669 } else if (!zend_jit_store_var_if_necessary(Dst, opline->result.var, res_addr, res_info)) { 14670 return 0; 14671 } 14672 } else { 14673 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG2w, TMP1 14674 | TRY_ADDREF res_info, REG2w, REG1, TMP1w 14675 } 14676 } else { 14677 | ZVAL_COPY_VALUE res_addr, MAY_BE_ANY, const_addr, MAY_BE_ANY, ZREG_REG0, ZREG_REG1, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 14678 | TRY_ADDREF MAY_BE_ANY, REG0w, REG1, TMP1w 14679 } 14680 14681 |.cold_code 14682 |9: 14683 | // SAVE_OPLINE(); 14684 | SET_EX_OPLINE opline, REG0 14685 | // zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num OPLINE_CC EXECUTE_DATA_CC); 14686 | LOAD_ADDR FCARG1x, zv 14687 | LOAD_32BIT_VAL FCARG2w, opline->op1.num 14688 | EXT_CALL zend_jit_get_constant, REG0 14689 | mov REG0, RETVALx 14690 | // ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); 14691 | cbnz REG0, <8 14692 | b ->exception_handler 14693 |.code 14694 return 1; 14695} 14696 14697static 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) 14698{ 14699 HashTable *ht = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2)); 14700 zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); 14701 14702 ZEND_ASSERT(opline->op1_type != IS_VAR && opline->op1_type != IS_TMP_VAR); 14703 ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_STRING); 14704 14705 | // result = zend_hash_find_ex(ht, Z_STR_P(op1), OP1_TYPE == IS_CONST); 14706 | LOAD_ADDR FCARG1x, ht 14707 if (opline->op1_type != IS_CONST) { 14708 | GET_ZVAL_PTR FCARG2x, op1_addr, TMP1 14709 | EXT_CALL zend_hash_find, REG0 14710 } else { 14711 zend_string *str = Z_STR_P(RT_CONSTANT(opline, opline->op1)); 14712 | LOAD_ADDR FCARG2x, str 14713 | EXT_CALL zend_hash_find_known_hash, REG0 14714 } 14715 if (exit_addr) { 14716 if (smart_branch_opcode == ZEND_JMPZ) { 14717 | cbz RETVALx, &exit_addr 14718 } else { 14719 | cbnz RETVALx, &exit_addr 14720 } 14721 } else if (smart_branch_opcode) { 14722 if (smart_branch_opcode == ZEND_JMPZ) { 14723 | cbz RETVALx, =>target_label 14724 } else if (smart_branch_opcode == ZEND_JMPNZ) { 14725 | cbnz RETVALx, =>target_label 14726 } else { 14727 ZEND_UNREACHABLE(); 14728 } 14729 } else { 14730 | tst RETVALx, RETVALx 14731 | cset REG0w, ne 14732 | add REG0w, REG0w, #IS_FALSE 14733 | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 14734 } 14735 14736 return 1; 14737} 14738 14739static int zend_jit_rope(dasm_State **Dst, const zend_op *opline, uint32_t op2_info) 14740{ 14741 uint32_t offset; 14742 14743 offset = (opline->opcode == ZEND_ROPE_INIT) ? 14744 opline->result.var : 14745 opline->op1.var + opline->extended_value * sizeof(zend_string*); 14746 14747 if (opline->op2_type == IS_CONST) { 14748 zval *zv = RT_CONSTANT(opline, opline->op2); 14749 zend_string *str; 14750 14751 ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); 14752 str = Z_STR_P(zv); 14753 | LOAD_ADDR REG0, str 14754 | MEM_ACCESS_64_WITH_UOFFSET str, REG0, FP, offset, TMP1 14755 } else { 14756 zend_jit_addr op2_addr = OP2_ADDR(); 14757 14758 ZEND_ASSERT((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING); 14759 14760 | GET_ZVAL_PTR REG1, op2_addr, TMP1 14761 | MEM_ACCESS_64_WITH_UOFFSET str, REG1, FP, offset, TMP1 14762 if (opline->op2_type == IS_CV) { 14763 | GET_ZVAL_TYPE_INFO REG0w, op2_addr, TMP1 14764 | TRY_ADDREF op2_info, REG0w, REG1, TMP1w 14765 } 14766 } 14767 14768 if (opline->opcode == ZEND_ROPE_END) { 14769 zend_jit_addr res_addr = RES_ADDR(); 14770 14771 | ADD_SUB_64_WITH_CONST add, FCARG1x, FP, opline->op1.var, TMP1 14772 | LOAD_32BIT_VAL FCARG2w, opline->extended_value 14773 | EXT_CALL zend_jit_rope_end, TMP1 14774 | SET_ZVAL_PTR res_addr, RETVALx, TMP1 14775 | SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX, TMP1w, TMP2 14776 } 14777 14778 return 1; 14779} 14780 14781static bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_addr) 14782{ 14783 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 14784 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 14785 14786 if (!exit_addr) { 14787 return 0; 14788 } 14789 | IF_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr, ZREG_TMP1 14790 14791 return 1; 14792} 14793 14794static 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) 14795{ 14796 zend_jit_addr var_addr = *var_addr_ptr; 14797 uint32_t var_info = *var_info_ptr; 14798 const void *exit_addr = NULL; 14799 14800 if (add_ref_guard || add_type_guard) { 14801 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 14802 14803 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 14804 if (!exit_addr) { 14805 return 0; 14806 } 14807 } 14808 14809 if (add_ref_guard) { 14810 | IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr, ZREG_TMP1 14811 } 14812 if (opline->opcode == ZEND_INIT_METHOD_CALL && opline->op1_type == IS_VAR) { 14813 /* Hack: Convert reference to regular value to simplify JIT code for INIT_METHOD_CALL */ 14814 if (Z_REG(var_addr) != ZREG_FCARG1 || Z_OFFSET(var_addr) != 0) { 14815 | LOAD_ZVAL_ADDR FCARG1x, var_addr 14816 } 14817 | EXT_CALL zend_jit_unref_helper, REG0 14818 } else { 14819 | GET_ZVAL_PTR FCARG1x, var_addr, TMP1 14820 var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, offsetof(zend_reference, val)); 14821 *var_addr_ptr = var_addr; 14822 } 14823 14824 if (var_type != IS_UNKNOWN) { 14825 var_type &= ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED); 14826 } 14827 if (add_type_guard 14828 && var_type != IS_UNKNOWN 14829 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) { 14830 | IF_NOT_ZVAL_TYPE var_addr, var_type, &exit_addr, ZREG_TMP1 14831 14832 ZEND_ASSERT(var_info & (1 << var_type)); 14833 if (var_type < IS_STRING) { 14834 var_info = (1 << var_type); 14835 } else if (var_type != IS_ARRAY) { 14836 var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN)); 14837 } else { 14838 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)); 14839 } 14840 14841 *var_info_ptr = var_info; 14842 } else { 14843 var_info &= ~MAY_BE_REF; 14844 *var_info_ptr = var_info; 14845 } 14846 *var_info_ptr |= MAY_BE_GUARD; /* prevent generation of specialized zval dtor */ 14847 14848 return 1; 14849} 14850 14851static 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) 14852{ 14853 zend_jit_addr var_addr = *var_addr_ptr; 14854 uint32_t var_info = *var_info_ptr; 14855 int32_t exit_point; 14856 const void *exit_addr; 14857 14858 if (add_indirect_guard) { 14859 int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); 14860 const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); 14861 14862 if (!exit_addr) { 14863 return 0; 14864 } 14865 | IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr, ZREG_TMP1 14866 | GET_ZVAL_PTR FCARG1x, var_addr, TMP1 14867 } else { 14868 /* May be already loaded into FCARG1a or RAX by previous FETCH_OBJ_W/DIM_W */ 14869 if (opline->op1_type != IS_VAR || 14870 (opline-1)->result_type != IS_VAR || 14871 (opline-1)->result.var != opline->op1.var || 14872 (opline-1)->op1_type == IS_VAR || 14873 (opline-1)->op2_type == IS_VAR || 14874 (opline-1)->op2_type == IS_TMP_VAR) { 14875 | GET_ZVAL_PTR FCARG1x, var_addr, TMP1 14876 } else if ((opline-1)->opcode == ZEND_FETCH_DIM_W || (opline-1)->opcode == ZEND_FETCH_DIM_RW) { 14877 | mov FCARG1x, REG0 14878 } 14879 } 14880 *var_info_ptr &= ~MAY_BE_INDIRECT; 14881 var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1, 0); 14882 *var_addr_ptr = var_addr; 14883 14884 if (var_type != IS_UNKNOWN) { 14885 var_type &= ~(IS_TRACE_INDIRECT|IS_TRACE_PACKED); 14886 } 14887 if (!(var_type & IS_TRACE_REFERENCE) 14888 && var_type != IS_UNKNOWN 14889 && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) { 14890 exit_point = zend_jit_trace_get_exit_point(opline, 0); 14891 exit_addr = zend_jit_trace_get_exit_addr(exit_point); 14892 14893 if (!exit_addr) { 14894 return 0; 14895 } 14896 14897 | IF_NOT_Z_TYPE FCARG1x, var_type, &exit_addr, TMP1w 14898 14899 //var_info = zend_jit_trace_type_to_info_ex(var_type, var_info); 14900 ZEND_ASSERT(var_info & (1 << var_type)); 14901 if (var_type < IS_STRING) { 14902 var_info = (1 << var_type); 14903 } else if (var_type != IS_ARRAY) { 14904 var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN)); 14905 } else { 14906 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)); 14907 } 14908 14909 *var_info_ptr = var_info; 14910 } 14911 14912 return 1; 14913} 14914 14915static 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) 14916{ 14917 if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) { 14918 return 0; 14919 } 14920 14921 switch (opline->opcode) { 14922 case ZEND_QM_ASSIGN: 14923 case ZEND_SEND_VAR: 14924 case ZEND_ASSIGN: 14925 case ZEND_PRE_INC: 14926 case ZEND_PRE_DEC: 14927 case ZEND_POST_INC: 14928 case ZEND_POST_DEC: 14929 return 1; 14930 case ZEND_ADD: 14931 case ZEND_SUB: 14932 case ZEND_MUL: 14933 case ZEND_BW_OR: 14934 case ZEND_BW_AND: 14935 case ZEND_BW_XOR: 14936 case ZEND_SL: 14937 case ZEND_SR: 14938 if (def_var == ssa_op->result_def && 14939 use_var == ssa_op->op1_use) { 14940 return 1; 14941 } 14942 break; 14943 default: 14944 break; 14945 } 14946 return 0; 14947} 14948 14949static 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) 14950{ 14951 uint32_t op1_info, op2_info; 14952 14953 switch (opline->opcode) { 14954 case ZEND_SEND_VAR: 14955 case ZEND_SEND_VAL: 14956 case ZEND_SEND_VAL_EX: 14957 return (opline->op2_type != IS_CONST); 14958 case ZEND_QM_ASSIGN: 14959 case ZEND_IS_SMALLER: 14960 case ZEND_IS_SMALLER_OR_EQUAL: 14961 case ZEND_IS_EQUAL: 14962 case ZEND_IS_NOT_EQUAL: 14963 case ZEND_IS_IDENTICAL: 14964 case ZEND_IS_NOT_IDENTICAL: 14965 case ZEND_CASE: 14966 return 1; 14967 case ZEND_RETURN: 14968 return (op_array->type != ZEND_EVAL_CODE && op_array->function_name); 14969 case ZEND_ASSIGN: 14970 op1_info = OP1_INFO(); 14971 op2_info = OP2_INFO(); 14972 return 14973 opline->op1_type == IS_CV && 14974 !(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_RESOURCE|MAY_BE_OBJECT|MAY_BE_REF)) && 14975 !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))); 14976 case ZEND_ADD: 14977 case ZEND_SUB: 14978 case ZEND_MUL: 14979 op1_info = OP1_INFO(); 14980 op2_info = OP2_INFO(); 14981 return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))); 14982 case ZEND_BW_OR: 14983 case ZEND_BW_AND: 14984 case ZEND_BW_XOR: 14985 case ZEND_SL: 14986 case ZEND_SR: 14987 case ZEND_MOD: 14988 op1_info = OP1_INFO(); 14989 op2_info = OP2_INFO(); 14990 return !((op1_info | op2_info) & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG)); 14991 case ZEND_PRE_INC: 14992 case ZEND_PRE_DEC: 14993 case ZEND_POST_INC: 14994 case ZEND_POST_DEC: 14995 op1_info = OP1_INFO(); 14996 op2_info = OP1_DEF_INFO(); 14997 return opline->op1_type == IS_CV 14998 && !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - MAY_BE_LONG)) 14999 && (op2_info & MAY_BE_LONG); 15000 case ZEND_STRLEN: 15001 op1_info = OP1_INFO(); 15002 return (opline->op1_type & (IS_CV|IS_CONST)) 15003 && (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_STRING; 15004 case ZEND_COUNT: 15005 op1_info = OP1_INFO(); 15006 return (opline->op1_type & (IS_CV|IS_CONST)) 15007 && (op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) == MAY_BE_ARRAY; 15008 case ZEND_JMPZ: 15009 case ZEND_JMPNZ: 15010 if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) { 15011 if (!ssa->cfg.map) { 15012 return 0; 15013 } 15014 if (opline > op_array->opcodes + ssa->cfg.blocks[ssa->cfg.map[opline-op_array->opcodes]].start && 15015 ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) { 15016 return 0; 15017 } 15018 } 15019 ZEND_FALLTHROUGH; 15020 case ZEND_BOOL: 15021 case ZEND_BOOL_NOT: 15022 case ZEND_JMPZ_EX: 15023 case ZEND_JMPNZ_EX: 15024 return 1; 15025 case ZEND_FETCH_CONSTANT: 15026 return 1; 15027 case ZEND_FETCH_DIM_R: 15028 op1_info = OP1_INFO(); 15029 op2_info = OP2_INFO(); 15030 if (trace 15031 && trace->op1_type != IS_UNKNOWN 15032 && (trace->op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)) == IS_ARRAY) { 15033 op1_info &= ~((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY); 15034 } 15035 return ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) && 15036 (!(opline->op1_type & (IS_TMP_VAR|IS_VAR)) || !(op1_info & MAY_BE_RC1)) && 15037 (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) || 15038 (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) && 15039 (!(opline->op2_type & (IS_TMP_VAR|IS_VAR)) || !(op2_info & MAY_BE_RC1)))); 15040 } 15041 return 0; 15042} 15043 15044static bool zend_jit_var_supports_reg(zend_ssa *ssa, int var) 15045{ 15046 if (ssa->vars[var].no_val) { 15047 /* we don't need the value */ 15048 return 0; 15049 } 15050 15051 if (!(JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL)) { 15052 /* Disable global register allocation, 15053 * register allocation for SSA variables connected through Phi functions 15054 */ 15055 if (ssa->vars[var].definition_phi) { 15056 return 0; 15057 } 15058 if (ssa->vars[var].phi_use_chain) { 15059 zend_ssa_phi *phi = ssa->vars[var].phi_use_chain; 15060 do { 15061 if (!ssa->vars[phi->ssa_var].no_val) { 15062 return 0; 15063 } 15064 phi = zend_ssa_next_use_phi(ssa, var, phi); 15065 } while (phi); 15066 } 15067 } 15068 15069 if (((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_DOUBLE) && 15070 ((ssa->var_info[var].type & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_LONG)) { 15071 /* bad type */ 15072 return 0; 15073 } 15074 15075 return 1; 15076} 15077 15078static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, int var) 15079{ 15080 if (!zend_jit_var_supports_reg(ssa, var)) { 15081 return 0; 15082 } 15083 15084 if (ssa->vars[var].definition >= 0) { 15085 uint32_t def = ssa->vars[var].definition; 15086 if (!zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + def, ssa->ops + def, NULL)) { 15087 return 0; 15088 } 15089 } 15090 15091 if (ssa->vars[var].use_chain >= 0) { 15092 int use = ssa->vars[var].use_chain; 15093 15094 do { 15095 if (!zend_ssa_is_no_val_use(op_array->opcodes + use, ssa->ops + use, var) && 15096 !zend_jit_opline_supports_reg(op_array, ssa, op_array->opcodes + use, ssa->ops + use, NULL)) { 15097 return 0; 15098 } 15099 use = zend_ssa_next_use(ssa->ops, var, use); 15100 } while (use >= 0); 15101 } 15102 15103 return 1; 15104} 15105 15106static 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) 15107{ 15108 uint32_t op1_info, op2_info; 15109 15110 switch (opline->opcode) { 15111 case ZEND_FETCH_DIM_R: 15112 op1_info = OP1_INFO(); 15113 op2_info = OP2_INFO(); 15114 if (((opline->op1_type & (IS_TMP_VAR|IS_VAR)) && 15115 (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) || 15116 ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && 15117 (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)))) { 15118 return ZEND_REGSET(ZREG_FCARG1); 15119 } 15120 break; 15121 default: 15122 break; 15123 } 15124 15125 return ZEND_REGSET_EMPTY; 15126} 15127 15128static 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) 15129{ 15130 uint32_t op1_info, op2_info, res_info; 15131 zend_regset regset = ZEND_REGSET_SCRATCH; 15132 15133 switch (opline->opcode) { 15134 case ZEND_NOP: 15135 case ZEND_OP_DATA: 15136 case ZEND_JMP: 15137 case ZEND_RETURN: 15138 regset = ZEND_REGSET_EMPTY; 15139 break; 15140 case ZEND_QM_ASSIGN: 15141 if (ssa_op->op1_def == current_var || 15142 ssa_op->result_def == current_var) { 15143 regset = ZEND_REGSET_EMPTY; 15144 break; 15145 } 15146 /* break missing intentionally */ 15147 case ZEND_SEND_VAL: 15148 case ZEND_SEND_VAL_EX: 15149 if (opline->op2_type == IS_CONST) { 15150 break; 15151 } 15152 if (ssa_op->op1_use == current_var) { 15153 regset = ZEND_REGSET(ZREG_REG0); 15154 break; 15155 } 15156 op1_info = OP1_INFO(); 15157 if (!(op1_info & MAY_BE_UNDEF)) { 15158 if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) { 15159 regset = ZEND_REGSET(ZREG_FPR0); 15160 } else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) { 15161 regset = ZEND_REGSET(ZREG_REG0); 15162 } else { 15163 regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2)); 15164 } 15165 } 15166 break; 15167 case ZEND_SEND_VAR: 15168 if (opline->op2_type == IS_CONST) { 15169 break; 15170 } 15171 if (ssa_op->op1_use == current_var || 15172 ssa_op->op1_def == current_var) { 15173 regset = ZEND_REGSET_EMPTY; 15174 break; 15175 } 15176 op1_info = OP1_INFO(); 15177 if (!(op1_info & MAY_BE_UNDEF)) { 15178 if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) { 15179 regset = ZEND_REGSET(ZREG_FPR0); 15180 } else if ((op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) { 15181 } else { 15182 regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2)); 15183 if (op1_info & MAY_BE_REF) { 15184 ZEND_REGSET_INCL(regset, ZREG_REG1); 15185 } 15186 } 15187 } 15188 break; 15189 case ZEND_ASSIGN: 15190 if (ssa_op->op2_use == current_var || 15191 ssa_op->op2_def == current_var || 15192 ssa_op->op1_def == current_var || 15193 ssa_op->result_def == current_var) { 15194 regset = ZEND_REGSET_EMPTY; 15195 break; 15196 } 15197 op1_info = OP1_INFO(); 15198 op2_info = OP2_INFO(); 15199 if (opline->op1_type == IS_CV 15200 && !(op2_info & MAY_BE_UNDEF) 15201 && !(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { 15202 if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_DOUBLE) { 15203 regset = ZEND_REGSET(ZREG_FPR0); 15204 } else if ((op2_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_LONG) { 15205 regset = ZEND_REGSET(ZREG_REG0); 15206 } else { 15207 regset = ZEND_REGSET_UNION(ZEND_REGSET(ZREG_REG0), ZEND_REGSET(ZREG_REG2)); 15208 } 15209 } 15210 break; 15211 case ZEND_PRE_INC: 15212 case ZEND_PRE_DEC: 15213 case ZEND_POST_INC: 15214 case ZEND_POST_DEC: 15215 if (ssa_op->op1_use == current_var || 15216 ssa_op->op1_def == current_var || 15217 ssa_op->result_def == current_var) { 15218 regset = ZEND_REGSET_EMPTY; 15219 break; 15220 } 15221 op1_info = OP1_INFO(); 15222 if (opline->op1_type == IS_CV 15223 && (op1_info & MAY_BE_LONG) 15224 && !(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) { 15225 regset = ZEND_REGSET_EMPTY; 15226 if (op1_info & MAY_BE_DOUBLE) { 15227 regset = ZEND_REGSET(ZREG_FPR0); 15228 } 15229 if (opline->result_type != IS_UNUSED && (op1_info & MAY_BE_LONG)) { 15230 ZEND_REGSET_INCL(regset, ZREG_REG1); 15231 } 15232 } 15233 break; 15234 case ZEND_ADD: 15235 case ZEND_SUB: 15236 case ZEND_MUL: 15237 op1_info = OP1_INFO(); 15238 op2_info = OP2_INFO(); 15239 if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) && 15240 !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) { 15241 15242 regset = ZEND_REGSET_EMPTY; 15243 if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) { 15244 if (ssa_op->result_def != current_var && 15245 (ssa_op->op1_use != current_var || !last_use)) { 15246 ZEND_REGSET_INCL(regset, ZREG_REG0); 15247 } 15248 res_info = RES_INFO(); 15249 if (res_info & MAY_BE_DOUBLE) { 15250 ZEND_REGSET_INCL(regset, ZREG_REG0); 15251 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15252 ZEND_REGSET_INCL(regset, ZREG_FPR1); 15253 } else if (res_info & MAY_BE_GUARD) { 15254 ZEND_REGSET_INCL(regset, ZREG_REG0); 15255 } 15256 } 15257 if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) { 15258 if (ssa_op->result_def != current_var) { 15259 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15260 } 15261 } 15262 if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) { 15263 if (zend_is_commutative(opline->opcode)) { 15264 if (ssa_op->result_def != current_var) { 15265 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15266 } 15267 } else { 15268 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15269 if (ssa_op->result_def != current_var && 15270 (ssa_op->op1_use != current_var || !last_use)) { 15271 ZEND_REGSET_INCL(regset, ZREG_FPR1); 15272 } 15273 } 15274 } 15275 if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) { 15276 if (ssa_op->result_def != current_var && 15277 (ssa_op->op1_use != current_var || !last_use) && 15278 (!zend_is_commutative(opline->opcode) || ssa_op->op2_use != current_var || !last_use)) { 15279 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15280 } 15281 } 15282 } 15283 break; 15284 case ZEND_BW_OR: 15285 case ZEND_BW_AND: 15286 case ZEND_BW_XOR: 15287 case ZEND_SL: 15288 case ZEND_SR: 15289 case ZEND_MOD: 15290 op1_info = OP1_INFO(); 15291 op2_info = OP2_INFO(); 15292 if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG)) && 15293 !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) { 15294 regset = ZEND_REGSET_EMPTY; 15295 if (ssa_op->result_def != current_var && 15296 (ssa_op->op1_use != current_var || !last_use)) { 15297 ZEND_REGSET_INCL(regset, ZREG_REG0); 15298 } 15299 } 15300 break; 15301 case ZEND_IS_SMALLER: 15302 case ZEND_IS_SMALLER_OR_EQUAL: 15303 case ZEND_IS_EQUAL: 15304 case ZEND_IS_NOT_EQUAL: 15305 case ZEND_IS_IDENTICAL: 15306 case ZEND_IS_NOT_IDENTICAL: 15307 case ZEND_CASE: 15308 op1_info = OP1_INFO(); 15309 op2_info = OP2_INFO(); 15310 if (!(op1_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) && 15311 !(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE)))) { 15312 regset = ZEND_REGSET_EMPTY; 15313 if (!(opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ))) { 15314 ZEND_REGSET_INCL(regset, ZREG_REG0); 15315 } 15316 if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && 15317 opline->op1_type != IS_CONST && opline->op2_type != IS_CONST) { 15318 if (ssa_op->op1_use != current_var && 15319 ssa_op->op2_use != current_var) { 15320 ZEND_REGSET_INCL(regset, ZREG_REG0); 15321 } 15322 } 15323 if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) { 15324 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15325 } 15326 if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) { 15327 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15328 } 15329 if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) { 15330 if (ssa_op->op1_use != current_var && 15331 ssa_op->op2_use != current_var) { 15332 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15333 } 15334 } 15335 } 15336 break; 15337 case ZEND_BOOL: 15338 case ZEND_BOOL_NOT: 15339 case ZEND_JMPZ: 15340 case ZEND_JMPNZ: 15341 case ZEND_JMPZ_EX: 15342 case ZEND_JMPNZ_EX: 15343 op1_info = OP1_INFO(); 15344 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)))) { 15345 regset = ZEND_REGSET_EMPTY; 15346 if (op1_info & MAY_BE_DOUBLE) { 15347 ZEND_REGSET_INCL(regset, ZREG_FPR0); 15348 } 15349 if (opline->opcode == ZEND_BOOL || 15350 opline->opcode == ZEND_BOOL_NOT || 15351 opline->opcode == ZEND_JMPZ_EX || 15352 opline->opcode == ZEND_JMPNZ_EX) { 15353 ZEND_REGSET_INCL(regset, ZREG_REG0); 15354 } 15355 } 15356 break; 15357 case ZEND_DO_UCALL: 15358 case ZEND_DO_FCALL: 15359 case ZEND_DO_FCALL_BY_NAME: 15360 case ZEND_INCLUDE_OR_EVAL: 15361 case ZEND_GENERATOR_CREATE: 15362 case ZEND_YIELD: 15363 case ZEND_YIELD_FROM: 15364 regset = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP); 15365 break; 15366 default: 15367 break; 15368 } 15369 15370 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { 15371 if (ssa_op == ssa->ops 15372 && JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].op == ZEND_JIT_TRACE_INIT_CALL 15373 && (JIT_G(current_trace)[ZEND_JIT_TRACE_START_REC_SIZE].info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) { 15374 ZEND_REGSET_INCL(regset, ZREG_REG0); 15375 ZEND_REGSET_INCL(regset, ZREG_REG1); 15376 } 15377 } 15378 15379 return regset; 15380} 15381 15382static size_t dasm_venners_size = 0; 15383void **dasm_labels_veneers = NULL; 15384 15385static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int *b, uint32_t *cp, ptrdiff_t offset) 15386{ 15387 void *veneer; 15388 ptrdiff_t na; 15389 int n, m; 15390 15391 /* try to reuse veneers for global labels */ 15392 if ((ins >> 16) == DASM_REL_LG 15393 && *(b-1) < 0 15394 && dasm_labels_veneers[-*(b-1)]) { 15395 15396 veneer = dasm_labels_veneers[-*(b-1)]; 15397 na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4; 15398 n = (int)na; 15399 15400 /* check if we can jump to veneer */ 15401 if ((ptrdiff_t)n != na) { 15402 /* pass */ 15403 } else if (!(ins & 0xf800)) { /* B, BL */ 15404 if ((n & 3) == 0 && ((n+0x08000000) >> 28) == 0) { 15405 return n; 15406 } 15407 } else if ((ins & 0x800)) { /* B.cond, CBZ, CBNZ, LDR* literal */ 15408 if ((n & 3) == 0 && ((n+0x00100000) >> 21) == 0) { 15409 return n; 15410 } 15411 } else if ((ins & 0x3000) == 0x2000) { /* ADR */ 15412 /* pass */ 15413 } else if ((ins & 0x3000) == 0x3000) { /* ADRP */ 15414 /* pass */ 15415 } else if ((ins & 0x1000)) { /* TBZ, TBNZ */ 15416 if ((n & 3) == 0 && ((n+0x00008000) >> 16) == 0) { 15417 return n; 15418 } 15419 } 15420 } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE 15421 && (ins >> 16) == DASM_REL_A) { 15422 ptrdiff_t addr = (((ptrdiff_t)(*(b-1))) << 32) | (unsigned int)(*(b-2)); 15423 15424 if ((void*)addr >= dasm_buf && (void*)addr < dasm_end) { 15425 uint32_t exit_point = zend_jit_trace_find_exit_point((void*)addr); 15426 zend_jit_trace_info *t = zend_jit_get_current_trace_info(); 15427 15428 if (exit_point != (uint32_t)-1) { 15429 /* Use exit points table */ 15430 15431 ZEND_ASSERT(exit_point < t->exit_count); 15432 15433 veneer = (char*)buffer + dasm_getpclabel(&Dst, 1) - (t->exit_count - exit_point) * 4; 15434 na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4; 15435 n = (int)na; 15436 15437 /* check if we can jump to veneer */ 15438 if ((ptrdiff_t)n != na) { 15439 ZEND_ASSERT(0); 15440 return 0; 15441 } else if (!(ins & 0xf800)) { /* B, BL */ 15442 if ((n & 3) != 0 || ((n+0x08000000) >> 28) != 0) { 15443 ZEND_ASSERT(0); 15444 return 0; 15445 } 15446 } else if ((ins & 0x800)) { /* B.cond, CBZ, CBNZ, LDR* literal */ 15447 if ((n & 3) != 0 || ((n+0x00100000) >> 21) != 0) { 15448 ZEND_ASSERT(0); 15449 return 0; 15450 } 15451 } else if ((ins & 0x3000) == 0x2000) { /* ADR */ 15452 ZEND_ASSERT(0); 15453 return 0; 15454 } else if ((ins & 0x3000) == 0x3000) { /* ADRP */ 15455 ZEND_ASSERT(0); 15456 return 0; 15457 } else if ((ins & 0x1000)) { /* TBZ, TBNZ */ 15458 if ((n & 3) != 0 || ((n+0x00008000) >> 16) != 0) { 15459 ZEND_ASSERT(0); 15460 return 0; 15461 } 15462 } else { 15463 ZEND_ASSERT(0); 15464 return 0; 15465 } 15466 return n; 15467 } 15468 } 15469 } 15470 15471 veneer = (char*)buffer + (Dst->codesize + dasm_venners_size); 15472 15473 if (veneer > dasm_end) { 15474 return 0; /* jit_buffer_size overflow */ 15475 } 15476 15477 na = (ptrdiff_t)veneer - (ptrdiff_t)cp + 4; 15478 n = (int)na; 15479 15480 /* check if we can jump to veneer */ 15481 if ((ptrdiff_t)n != na) { 15482 ZEND_ASSERT(0); 15483 return 0; 15484 } else if (!(ins & 0xf800)) { /* B, BL */ 15485 if ((n & 3) != 0 || ((n+0x08000000) >> 28) != 0) { 15486 ZEND_ASSERT(0); 15487 return 0; 15488 } 15489 } else if ((ins & 0x800)) { /* B.cond, CBZ, CBNZ, LDR* literal */ 15490 if ((n & 3) != 0 || ((n+0x00100000) >> 21) != 0) { 15491 ZEND_ASSERT(0); 15492 return 0; 15493 } 15494 } else if ((ins & 0x3000) == 0x2000) { /* ADR */ 15495 ZEND_ASSERT(0); 15496 return 0; 15497 } else if ((ins & 0x3000) == 0x3000) { /* ADRP */ 15498 ZEND_ASSERT(0); 15499 return 0; 15500 } else if ((ins & 0x1000)) { /* TBZ, TBNZ */ 15501 if ((n & 3) != 0 || ((n+0x00008000) >> 16) != 0) { 15502 ZEND_ASSERT(0); 15503 return 0; 15504 } 15505 } else if ((ins & 0x8000)) { /* absolute */ 15506 ZEND_ASSERT(0); 15507 return 0; 15508 } else { 15509 ZEND_ASSERT(0); 15510 return 0; 15511 } 15512 15513 // TODO: support for long veneers (above 128MB) ??? 15514 15515 /* check if we can use B to jump from veneer */ 15516 na = (ptrdiff_t)cp + offset - (ptrdiff_t)veneer - 4; 15517 m = (int)na; 15518 if ((ptrdiff_t)m != na) { 15519 ZEND_ASSERT(0); 15520 return 0; 15521 } else if ((m & 3) != 0 || ((m+0x08000000) >> 28) != 0) { 15522 ZEND_ASSERT(0); 15523 return 0; 15524 } 15525 15526 /* generate B instruction */ 15527 *(uint32_t*)veneer = 0x14000000 | ((m >> 2) & 0x03ffffff); 15528 dasm_venners_size += 4; 15529 15530 if ((ins >> 16) == DASM_REL_LG 15531 && *(b-1) < 0) { 15532 /* reuse this veneer for the future jumps to global label */ 15533 dasm_labels_veneers[-*(b-1)] = veneer; 15534 /* Dst->globals[*(b-1)] = veneer; */ 15535 15536#ifdef HAVE_DISASM 15537 if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM) { 15538 const char *name = zend_jit_disasm_find_symbol((ptrdiff_t)cp + offset - 4, (int64_t *)(&offset)); 15539 15540 if (name && !offset) { 15541 if (strstr(name, "@veneer") == NULL) { 15542 char *new_name; 15543 15544 zend_spprintf(&new_name, 0, "%s@veneer", name); 15545 zend_jit_disasm_add_symbol(new_name, (uint64_t)(uintptr_t)veneer, 4); 15546 efree(new_name); 15547 } else { 15548 zend_jit_disasm_add_symbol(name, (uint64_t)(uintptr_t)veneer, 4); 15549 } 15550 } 15551 } 15552#endif 15553 } 15554 15555 return n; 15556} 15557 15558/* 15559 * Local variables: 15560 * tab-width: 4 15561 * c-basic-offset: 4 15562 * indent-tabs-mode: t 15563 * End: 15564 */ 15565