php-src/ext/opcache/jit/zend_jit_x86.h
2020-07-17 00:40:10 +03:00

331 lines
9.9 KiB
C

/*
+----------------------------------------------------------------------+
| Zend JIT |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov <dmitry@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef HAVE_JIT_X86_H
#define HAVE_JIT_X86_H
typedef enum _zend_reg {
ZREG_NONE = -1,
ZREG_R0,
ZREG_R1,
ZREG_R2,
ZREG_R3,
ZREG_R4,
ZREG_R5,
ZREG_R6,
ZREG_R7,
#if defined(__x86_64__) || defined(_WIN64)
ZREG_R8,
ZREG_R9,
ZREG_R10,
ZREG_R11,
ZREG_R12,
ZREG_R13,
ZREG_R14,
ZREG_R15,
#endif
ZREG_XMM0,
ZREG_XMM1,
ZREG_XMM2,
ZREG_XMM3,
ZREG_XMM4,
ZREG_XMM5,
ZREG_XMM6,
ZREG_XMM7,
#if defined(__x86_64__) || defined(_WIN64)
ZREG_XMM8,
ZREG_XMM9,
ZREG_XMM10,
ZREG_XMM11,
ZREG_XMM12,
ZREG_XMM13,
ZREG_XMM14,
ZREG_XMM15,
#endif
ZREG_NUM,
ZREG_THIS, /* used for delayed FETCH_THIS deoptimization */
/* pseudo constants used by deoptimizer */
ZREG_LONG_MIN_MINUS_1,
ZREG_LONG_MIN,
ZREG_LONG_MAX,
ZREG_LONG_MAX_PLUS_1,
ZREG_NULL,
ZREG_ZVAL_TRY_ADDREF,
ZREG_ZVAL_COPY_R0,
} zend_reg;
typedef struct _zend_jit_registers_buf {
#if defined(__x86_64__) || defined(_WIN64)
uint64_t r[16];
double xmm[16];
#else
uint32_t r[8];
double xmm[8];
#endif
} zend_jit_registers_buf;
#define ZREG_RAX ZREG_R0
#define ZREG_RCX ZREG_R1
#define ZREG_RDX ZREG_R2
#define ZREG_RBX ZREG_R3
#define ZREG_RSP ZREG_R4
#define ZREG_RBP ZREG_R5
#define ZREG_RSI ZREG_R6
#define ZREG_RDI ZREG_R7
#ifdef _WIN64
# define ZREG_FP ZREG_R14
# define ZREG_IP ZREG_R15
# define ZREG_RX ZREG_IP
# define ZREG_FCARG1a ZREG_RCX
# define ZREG_FCARG2a ZREG_RDX
#elif defined(__x86_64__)
# define ZREG_FP ZREG_R14
# define ZREG_IP ZREG_R15
# define ZREG_RX ZREG_IP
# define ZREG_FCARG1a ZREG_RDI
# define ZREG_FCARG2a ZREG_RSI
#else
# define ZREG_FP ZREG_RSI
# define ZREG_IP ZREG_RDI
# define ZREG_RX ZREG_IP
# define ZREG_FCARG1a ZREG_RCX
# define ZREG_FCARG2a ZREG_RDX
#endif
extern const char *zend_reg_name[];
typedef uint32_t zend_regset;
#define ZEND_REGSET_EMPTY 0
#define ZEND_REGSET_IS_EMPTY(regset) \
(regset == ZEND_REGSET_EMPTY)
#define ZEND_REGSET(reg) \
(1u << (reg))
#define ZEND_REGSET_INTERVAL(reg1, reg2) \
(((1u << ((reg2) - (reg1) + 1)) - 1) << (reg1))
#define ZEND_REGSET_IN(regset, reg) \
(((regset) & ZEND_REGSET(reg)) != 0)
#define ZEND_REGSET_INCL(regset, reg) \
(regset) |= ZEND_REGSET(reg)
#define ZEND_REGSET_EXCL(regset, reg) \
(regset) &= ~ZEND_REGSET(reg)
#define ZEND_REGSET_UNION(set1, set2) \
((set1) | (set2))
#define ZEND_REGSET_INTERSECTION(set1, set2) \
((set1) & (set2))
#define ZEND_REGSET_DIFFERENCE(set1, set2) \
((set1) & ~(set2))
#ifdef _WIN64
# define ZEND_REGSET_FIXED \
(ZEND_REGSET(ZREG_RSP) | ZEND_REGSET(ZREG_R14) | ZEND_REGSET(ZREG_R15))
# define ZEND_REGSET_GP \
ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_R0, ZREG_R15), ZEND_REGSET_FIXED)
# define ZEND_REGSET_FP \
ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_XMM0, ZREG_XMM15), ZEND_REGSET_FIXED)
# define ZEND_REGSET_SCRATCH \
(ZEND_REGSET(ZREG_RAX) | ZEND_REGSET(ZREG_RDX) | ZEND_REGSET(ZREG_RCX) | ZEND_REGSET_INTERVAL(ZREG_R8, ZREG_R11) | ZEND_REGSET_FP)
# define ZEND_REGSET_PRESERVED \
(ZEND_REGSET(ZREG_RBX) | ZEND_REGSET(ZREG_RBP) | ZEND_REGSET(ZREG_R12) | ZEND_REGSET(ZREG_R13) | ZEND_REGSET(ZREG_RDI) | ZEND_REGSET(ZREG_RSI))
#elif defined(__x86_64__)
# define ZEND_REGSET_FIXED \
(ZEND_REGSET(ZREG_RSP) | ZEND_REGSET(ZREG_R14) | ZEND_REGSET(ZREG_R15))
# define ZEND_REGSET_GP \
ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_R0, ZREG_R15), ZEND_REGSET_FIXED)
# define ZEND_REGSET_FP \
ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_XMM0, ZREG_XMM15), ZEND_REGSET_FIXED)
# define ZEND_REGSET_SCRATCH \
(ZEND_REGSET(ZREG_RAX) | ZEND_REGSET(ZREG_RDI) | ZEND_REGSET(ZREG_RSI) | ZEND_REGSET(ZREG_RDX) | ZEND_REGSET(ZREG_RCX) | ZEND_REGSET_INTERVAL(ZREG_R8, ZREG_R11) | ZEND_REGSET_FP)
# define ZEND_REGSET_PRESERVED \
(ZEND_REGSET(ZREG_RBX) | ZEND_REGSET(ZREG_RBP) | ZEND_REGSET(ZREG_R12) | ZEND_REGSET(ZREG_R13))
#else
# define ZEND_REGSET_FIXED \
(ZEND_REGSET(ZREG_RSP) | ZEND_REGSET(ZREG_RSI) | ZEND_REGSET(ZREG_RDI))
# define ZEND_REGSET_GP \
ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_R0, ZREG_R7), ZEND_REGSET_FIXED)
# define ZEND_REGSET_FP \
ZEND_REGSET_DIFFERENCE(ZEND_REGSET_INTERVAL(ZREG_XMM0, ZREG_XMM7), ZEND_REGSET_FIXED)
# define ZEND_REGSET_SCRATCH \
(ZEND_REGSET(ZREG_RAX) | ZEND_REGSET(ZREG_RCX) | ZEND_REGSET(ZREG_RDX) | ZEND_REGSET_FP)
# define ZEND_REGSET_PRESERVED \
(ZEND_REGSET(ZREG_RBX) | ZEND_REGSET(ZREG_RBP))
#endif
#ifndef _WIN32
#define ZEND_REGSET_FIRST(set) ((zend_reg)__builtin_ctz(set))
#define ZEND_REGSET_LAST(set) ((zend_reg)(__builtin_clz(set)^31)))
#else
#include <intrin.h>
uint32_t __inline __zend_jit_ctz( uint32_t value ) {
DWORD trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value)) {
return trailing_zero;
}
return 32;
}
uint32_t __inline __zend_jit_clz(uint32_t value) {
DWORD leading_zero = 0;
if (_BitScanReverse(&leading_zero, value)) {
return 31 - leading_zero;
}
return 32;
}
#define ZEND_REGSET_FIRST(set) ((zend_reg)__zend_jit_ctz(set))
#define ZEND_REGSET_LAST(set) ((zend_reg)(__zend_jit_clz(set)^31)))
#endif
#define ZEND_REGSET_FOREACH(set, reg) \
do { \
zend_regset _tmp = (set); \
while (!ZEND_REGSET_IS_EMPTY(_tmp)) { \
zend_reg _reg = ZEND_REGSET_FIRST(_tmp); \
ZEND_REGSET_EXCL(_tmp, _reg); \
reg = _reg; \
#define ZEND_REGSET_FOREACH_END() \
} \
} while (0)
typedef uintptr_t zend_jit_addr;
#define IS_CONST_ZVAL 0
#define IS_MEM_ZVAL 1
#define IS_REG 2
#define _ZEND_ADDR_MODE_MASK 0x3
#define _ZEND_ADDR_REG_SHIFT 2
#define _ZEND_ADDR_REG_MASK 0x3f
#define _ZEND_ADDR_OFFSET_SHIFT 8
#define _ZEND_ADDR_REG_STORE_BIT 8
#define _ZEND_ADDR_REG_LOAD_BIT 9
#define _ZEND_ADDR_REG_LAST_USE_BIT 10
#define ZEND_ADDR_CONST_ZVAL(zv) \
(((zend_jit_addr)(uintptr_t)(zv)) | IS_CONST_ZVAL)
#define ZEND_ADDR_MEM_ZVAL(reg, offset) \
((((zend_jit_addr)(uintptr_t)(offset)) << _ZEND_ADDR_OFFSET_SHIFT) | \
(((zend_jit_addr)(uintptr_t)(reg)) << _ZEND_ADDR_REG_SHIFT) | \
IS_MEM_ZVAL)
#define ZEND_ADDR_REG(reg) \
((((zend_jit_addr)(uintptr_t)(reg)) << _ZEND_ADDR_REG_SHIFT) | \
IS_REG)
#define Z_MODE(addr) (((addr) & _ZEND_ADDR_MODE_MASK))
#define Z_ZV(addr) ((zval*)(addr))
#define Z_OFFSET(addr) ((uint32_t)((addr)>>_ZEND_ADDR_OFFSET_SHIFT))
#define Z_REG(addr) ((zend_reg)(((addr)>>_ZEND_ADDR_REG_SHIFT) & _ZEND_ADDR_REG_MASK))
#define Z_STORE(addr) ((zend_reg)(((addr)>>_ZEND_ADDR_REG_STORE_BIT) & 1))
#define Z_LOAD(addr) ((zend_reg)(((addr)>>_ZEND_ADDR_REG_LOAD_BIT) & 1))
#define Z_LAST_USE(addr) ((zend_reg)(((addr)>>_ZEND_ADDR_REG_LAST_USE_BIT) & 1))
#define OP_REG_EX(reg, store, load, last_use) \
((reg) | \
((store) ? (1 << (_ZEND_ADDR_REG_STORE_BIT-_ZEND_ADDR_REG_SHIFT)) : 0) | \
((load) ? (1 << (_ZEND_ADDR_REG_LOAD_BIT-_ZEND_ADDR_REG_SHIFT)) : 0) | \
((last_use) ? (1 << (_ZEND_ADDR_REG_LAST_USE_BIT-_ZEND_ADDR_REG_SHIFT)) : 0) \
)
#define OP_REG(ssa_op, op) \
(ra && ssa_op->op >= 0 && ra[ssa_op->op] ? \
OP_REG_EX(ra[ssa_op->op]->reg, \
(ra[ssa_op->op]->flags & ZREG_STORE), \
(ra[ssa_op->op]->flags & ZREG_LOAD), \
zend_ival_is_last_use(ra[ssa_op->op], ssa_op - ssa->ops) \
) : ZREG_NONE)
static zend_always_inline zend_jit_addr _zend_jit_decode_op(zend_uchar op_type, znode_op op, const zend_op *opline, zend_reg reg)
{
if (op_type == IS_CONST) {
#if ZEND_USE_ABS_CONST_ADDR
return ZEND_ADDR_CONST_ZVAL(op.zv);
#else
return ZEND_ADDR_CONST_ZVAL(RT_CONSTANT(opline, op));
#endif
} else {
ZEND_ASSERT(op_type & (IS_CV|IS_TMP_VAR|IS_VAR));
if (reg != ZREG_NONE) {
return ZEND_ADDR_REG(reg);
} else {
return ZEND_ADDR_MEM_ZVAL(ZREG_FP, op.var);
}
}
}
#define OP_ADDR(opline, type, op) \
_zend_jit_decode_op((opline)->type, (opline)->op, opline, ZREG_NONE)
#define OP1_ADDR() \
OP_ADDR(opline, op1_type, op1)
#define OP2_ADDR() \
OP_ADDR(opline, op2_type, op2)
#define RES_ADDR() \
OP_ADDR(opline, result_type, result)
#define OP1_DATA_ADDR() \
OP_ADDR(opline + 1, op1_type, op1)
#define OP_REG_ADDR(opline, type, _op, _ssa_op) \
_zend_jit_decode_op((opline)->type, (opline)->_op, opline, \
OP_REG(ssa_op, _ssa_op))
#define OP1_REG_ADDR() \
OP_REG_ADDR(opline, op1_type, op1, op1_use)
#define OP2_REG_ADDR() \
OP_REG_ADDR(opline, op2_type, op2, op2_use)
#define RES_REG_ADDR() \
OP_REG_ADDR(opline, result_type, result, result_def)
#define OP1_DATA_REG_ADDR() \
OP_REG_ADDR(opline + 1, op1_type, op1, op1_use)
#define OP1_DEF_REG_ADDR() \
OP_REG_ADDR(opline, op1_type, op1, op1_def)
#define OP2_DEF_REG_ADDR() \
OP_REG_ADDR(opline, op2_type, op2, op2_def)
#define RES_USE_REG_ADDR() \
OP_REG_ADDR(opline, result_type, result, result_use)
#define OP1_DATA_DEF_REG_ADDR() \
OP_REG_ADDR(opline + 1, op1_type, op1, op1_def)
static zend_always_inline zend_bool zend_jit_same_addr(zend_jit_addr addr1, zend_jit_addr addr2)
{
if (addr1 == addr2) {
return 1;
} else if (Z_MODE(addr1) == IS_REG && Z_MODE(addr2) == IS_REG) {
return Z_REG(addr1) == Z_REG(addr2);
}
return 0;
}
#endif /* ZEND_JIT_X86_H */