mirror of
https://github.com/bdnugget/php-src.git
synced 2026-11-07 09:17:29 +00:00
JIT Refactoring:
- Move duplicate system independent functions from zend_jit_x86.dasc and zend_jit_arm64.dasc into zend_jit.c * zend_jit_is_constant_cmp_long_long() * zend_jit_needs_call_chain() * skip_valid_arguments() * zend_ssa_cv_info() * zend_jit_may_avoid_refcounting() * zend_get_known_property_info() * zend_may_be_dynamic_property() - Move common definitions from zend_jit_x86.dasc and zend_jit_arm64.dasc into zend_jit.c * GCC_GLOBAL_REGS * PROFITABILITY_CHECKS * BP_JIT_IS * sp_adj_kind * sp_adj[] - Remove unused DynAsm macros * ADDR_CMP (arm64) - Inline and remove DynAsm macros used once * LOAD_BASE_ADDR (arm64 and x86) * PUSH_BASE_ADDR (x86) * MEM_OP3_3 (x86) * SET_Z_PTR (x86) - Rename DynAsm macros * MEM_LOAD_OP_STORE -> MEM_UPDATE (arm64) * MEM_LOAD_OP_STORE_ZTS - > MEM_UPDATE_ZTS (arm64) * ADDR_OP1 -> PUSH_ADDR (x86) * ADDR_OP2_2 -> ADDR_STORE and ADDR_CMP (x86) * MEM_OP2_1_ZTS -> MEM_STORE_ZTS, MEM_CMP_ZTS, MEM_UPDATE_ZTRS (x86) * MEM_OP2_2 -> MEM_LOAD and MEM_LOAD_OP (x86) * MEM_OP2_2_ZTS -> MEM_LOAD_ZTS and MEM_LOAD_OP_ZTS (x86) * SSE_AVX_OP -> DOUBLE_CMP (x86) * SSE_GET_LONG -> DOUBLE_GET_LONG (x86) * SSE_GET_ZVAL_LVAL -> DOUBLE_GET_ZVAL_LVAL (x86) * SSE_GET_ZVAL_LVAL -> DOUBLE_GET_ZVAL_LVAL (x86) * SSE_GET_ZVAL_DVAL -> DOUBLE_GET_ZVAL_DVAL (x86) * SSE_SET_ZVAL_DVAL -> DOUBLE_SET_ZVAL_DVAL (x86)
This commit is contained in:
1 parent
5c3cb3583b
commit
c658216425
3 files changed
+780
-1292
No files matched your search
@@ -136,6 +136,18 @@ static zend_jit_trace_info *zend_jit_get_current_trace_info(void);
|
||||
static uint32_t zend_jit_trace_find_exit_point(const void* addr);
|
||||
#endif
|
||||
|
||||
static int zend_jit_assign_to_variable(dasm_State **Dst,
|
||||
const zend_op *opline,
|
||||
zend_jit_addr var_use_addr,
|
||||
zend_jit_addr var_addr,
|
||||
uint32_t var_info,
|
||||
uint32_t var_def_info,
|
||||
zend_uchar val_type,
|
||||
zend_jit_addr val_addr,
|
||||
uint32_t val_info,
|
||||
zend_jit_addr res_addr,
|
||||
bool check_exception);
|
||||
|
||||
static bool dominates(const zend_basic_block *blocks, int a, int b) {
|
||||
while (blocks[b].level > blocks[a].level) {
|
||||
b = blocks[b].idom;
|
||||
@@ -200,6 +212,450 @@ static bool zend_is_commutative(zend_uchar opcode)
|
||||
opcode == ZEND_BW_XOR;
|
||||
}
|
||||
|
||||
static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
|
||||
zend_ssa_range *op1_range,
|
||||
zend_jit_addr op1_addr,
|
||||
zend_ssa_range *op2_range,
|
||||
zend_jit_addr op2_addr,
|
||||
bool *result)
|
||||
{
|
||||
zend_long op1_min;
|
||||
zend_long op1_max;
|
||||
zend_long op2_min;
|
||||
zend_long op2_max;
|
||||
|
||||
if (op1_range) {
|
||||
op1_min = op1_range->min;
|
||||
op1_max = op1_range->max;
|
||||
} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
|
||||
ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG);
|
||||
op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (op2_range) {
|
||||
op2_min = op2_range->min;
|
||||
op2_max = op2_range->max;
|
||||
} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
|
||||
ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG);
|
||||
op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_IS_EQUAL:
|
||||
case ZEND_IS_IDENTICAL:
|
||||
case ZEND_CASE:
|
||||
case ZEND_CASE_STRICT:
|
||||
if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
} else if (op1_max < op2_min || op1_min > op2_max) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_IS_NOT_EQUAL:
|
||||
case ZEND_IS_NOT_IDENTICAL:
|
||||
if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
} else if (op1_max < op2_min || op1_min > op2_max) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_IS_SMALLER:
|
||||
if (op1_max < op2_min) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
} else if (op1_min >= op2_max) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_IS_SMALLER_OR_EQUAL:
|
||||
if (op1_max <= op2_min) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
} else if (op1_min > op2_max) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
ZEND_UNREACHABLE();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, const zend_op *opline, zend_jit_trace_rec *trace)
|
||||
{
|
||||
int skip;
|
||||
|
||||
if (trace) {
|
||||
zend_jit_trace_rec *p = trace;
|
||||
|
||||
ssa_op++;
|
||||
while (1) {
|
||||
if (p->op == ZEND_JIT_TRACE_VM) {
|
||||
switch (p->opline->opcode) {
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_JMP:
|
||||
case ZEND_JMPZNZ:
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_JMP_NULL:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
return 1;
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_FCALL:
|
||||
return 0;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
/* skip */
|
||||
break;
|
||||
default:
|
||||
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
ssa_op += zend_jit_trace_op_len(opline);
|
||||
} else if (p->op == ZEND_JIT_TRACE_ENTER ||
|
||||
p->op == ZEND_JIT_TRACE_BACK ||
|
||||
p->op == ZEND_JIT_TRACE_END) {
|
||||
return 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!call_info) {
|
||||
const zend_op *end = op_array->opcodes + op_array->last;
|
||||
|
||||
opline++;
|
||||
ssa_op++;
|
||||
skip = 1;
|
||||
while (opline != end) {
|
||||
if (!skip) {
|
||||
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
skip = 0;
|
||||
break;
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_JMP:
|
||||
case ZEND_JMPZNZ:
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_JMP_NULL:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
return 1;
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_FCALL:
|
||||
end = opline;
|
||||
if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
|
||||
/* INIT_FCALL and DO_FCALL in different BasicBlocks */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
opline++;
|
||||
ssa_op++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
const zend_op *end = call_info->caller_call_opline;
|
||||
|
||||
if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
|
||||
/* INIT_FCALL and DO_FCALL in different BasicBlocks */
|
||||
return 1;
|
||||
}
|
||||
|
||||
opline++;
|
||||
ssa_op++;
|
||||
skip = 1;
|
||||
while (opline != end) {
|
||||
if (skip) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
skip = 0;
|
||||
break;
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_UNPACK:
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
opline++;
|
||||
ssa_op++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ssa, const zend_call_info *call_info)
|
||||
{
|
||||
uint32_t num_args = 0;
|
||||
zend_function *func = call_info->callee_func;
|
||||
|
||||
/* It's okay to handle prototypes here, because they can only increase the accepted arguments.
|
||||
* Anything legal for the parent method is also legal for the parent method. */
|
||||
while (num_args < call_info->num_args) {
|
||||
zend_arg_info *arg_info = func->op_array.arg_info + num_args;
|
||||
|
||||
if (ZEND_TYPE_IS_SET(arg_info->type)) {
|
||||
if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) {
|
||||
zend_op *opline = call_info->arg_info[num_args].opline;
|
||||
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
|
||||
uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
|
||||
if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
num_args++;
|
||||
}
|
||||
return num_args;
|
||||
}
|
||||
|
||||
static uint32_t zend_ssa_cv_info(const zend_op_array *op_array, zend_ssa *ssa, uint32_t var)
|
||||
{
|
||||
uint32_t j, info;
|
||||
|
||||
if (ssa->vars && ssa->var_info) {
|
||||
info = ssa->var_info[var].type;
|
||||
for (j = op_array->last_var; j < ssa->vars_count; j++) {
|
||||
if (ssa->vars[j].var == var) {
|
||||
info |= ssa->var_info[j].type;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF |
|
||||
MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
|
||||
}
|
||||
|
||||
#ifdef ZEND_JIT_USE_RC_INFERENCE
|
||||
/* Refcount may be increased by RETURN opcode */
|
||||
if ((info & MAY_BE_RC1) && !(info & MAY_BE_RCN)) {
|
||||
for (j = 0; j < ssa->cfg.blocks_count; j++) {
|
||||
if ((ssa->cfg.blocks[j].flags & ZEND_BB_REACHABLE) &&
|
||||
ssa->cfg.blocks[j].len > 0) {
|
||||
const zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].start + ssa->cfg.blocks[j].len - 1;
|
||||
|
||||
if (opline->opcode == ZEND_RETURN) {
|
||||
if (opline->op1_type == IS_CV && opline->op1.var == EX_NUM_TO_VAR(var)) {
|
||||
info |= MAY_BE_RCN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool zend_jit_may_avoid_refcounting(const zend_op *opline)
|
||||
{
|
||||
switch (opline->opcode) {
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
if (!JIT_G(current_frame) ||
|
||||
!JIT_G(current_frame)->call->func ||
|
||||
!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
|
||||
return 0;
|
||||
}
|
||||
/* break missing intentionally */
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
if (opline->op2_type == IS_CONST
|
||||
&& Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING
|
||||
&& Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] != '\0') {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
if (!JIT_G(current_frame) ||
|
||||
!JIT_G(current_frame)->call->func ||
|
||||
!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
|
||||
return 0;
|
||||
}
|
||||
/* break missing intentionally */
|
||||
case ZEND_FETCH_DIM_R:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
return 1;
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
if (!(opline->extended_value & ZEND_ISEMPTY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
|
||||
{
|
||||
zend_property_info *info = NULL;
|
||||
|
||||
if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) ||
|
||||
!ce ||
|
||||
!(ce->ce_flags & ZEND_ACC_LINKED) ||
|
||||
(ce->ce_flags & ZEND_ACC_TRAIT) ||
|
||||
ce->create_object) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
if (ce->info.user.filename != filename) {
|
||||
/* class declaration might be changed independently */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ce->parent) {
|
||||
zend_class_entry *parent = ce->parent;
|
||||
|
||||
do {
|
||||
if (parent->type == ZEND_INTERNAL_CLASS) {
|
||||
break;
|
||||
} else if (parent->info.user.filename != filename) {
|
||||
/* some of parents class declarations might be changed independently */
|
||||
/* TODO: this check may be not enough, because even
|
||||
* in the same it's possible to conditionally define
|
||||
* few classes with the same name, and "parent" may
|
||||
* change from request to request.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
parent = parent->parent;
|
||||
} while (parent);
|
||||
}
|
||||
}
|
||||
|
||||
info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
|
||||
if (info == NULL ||
|
||||
!IS_VALID_PROPERTY_OFFSET(info->offset) ||
|
||||
(info->flags & ZEND_ACC_STATIC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(info->flags & ZEND_ACC_PUBLIC) &&
|
||||
(!on_this || info->ce != ce)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
|
||||
{
|
||||
zend_property_info *info;
|
||||
|
||||
if (!ce || (ce->ce_flags & ZEND_ACC_TRAIT)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
if (ce->info.user.filename != filename) {
|
||||
/* class declaration might be changed independently */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
|
||||
if (info == NULL ||
|
||||
!IS_VALID_PROPERTY_OFFSET(info->offset) ||
|
||||
(info->flags & ZEND_ACC_STATIC)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(info->flags & ZEND_ACC_PUBLIC) &&
|
||||
(!on_this || info->ce != ce)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OP_RANGE(ssa_op, opN) \
|
||||
(((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \
|
||||
ssa->var_info && \
|
||||
@@ -229,6 +685,45 @@ static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int
|
||||
# include "jit/zend_jit_oprofile.c"
|
||||
#endif
|
||||
|
||||
#include "Zend/zend_cpuinfo.h"
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
# include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
# define GCC_GLOBAL_REGS 1
|
||||
#else
|
||||
# define GCC_GLOBAL_REGS 0
|
||||
#endif
|
||||
|
||||
/* By default avoid JITing inline handlers if it does not seem profitable due to lack of
|
||||
* type information. Disabling this option allows testing some JIT handlers in the
|
||||
* presence of try/catch blocks, which prevent SSA construction. */
|
||||
#ifndef PROFITABILITY_CHECKS
|
||||
# define PROFITABILITY_CHECKS 1
|
||||
#endif
|
||||
|
||||
#define BP_JIT_IS 6 /* Used for ISSET_ISEMPTY_DIM_OBJ. see BP_VAR_*defines in Zend/zend_compile.h */
|
||||
|
||||
typedef enum _sp_adj_kind {
|
||||
SP_ADJ_NONE,
|
||||
SP_ADJ_RET,
|
||||
SP_ADJ_VM,
|
||||
SP_ADJ_JIT,
|
||||
SP_ADJ_ASSIGN,
|
||||
SP_ADJ_LAST
|
||||
} sp_adj_kind;
|
||||
|
||||
static int sp_adj[SP_ADJ_LAST];
|
||||
|
||||
/* The generated code may contain tautological comparisons, ignore them. */
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wtautological-compare"
|
||||
# pragma clang diagnostic ignored "-Wstring-compare"
|
||||
#endif
|
||||
|
||||
#if ZEND_JIT_TARGET_X86
|
||||
# include "jit/zend_jit_vtune.c"
|
||||
# include "jit/zend_jit_x86.c"
|
||||
@@ -236,6 +731,10 @@ static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int
|
||||
# include "jit/zend_jit_arm64.c"
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
# include <Windows.h>
|
||||
#else
|
||||
|
||||
@@ -85,16 +85,42 @@
|
||||
#define TMP_ZVAL_OFFSET 16
|
||||
#define DASM_ALIGNMENT 16
|
||||
|
||||
typedef enum _sp_adj_kind {
|
||||
SP_ADJ_NONE,
|
||||
SP_ADJ_RET,
|
||||
SP_ADJ_VM,
|
||||
SP_ADJ_JIT,
|
||||
SP_ADJ_ASSIGN,
|
||||
SP_ADJ_LAST
|
||||
} sp_adj_kind;
|
||||
const char* zend_reg_name[] = {
|
||||
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
|
||||
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
|
||||
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
|
||||
"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
|
||||
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
|
||||
"d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
|
||||
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
|
||||
"d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
|
||||
};
|
||||
|
||||
static int sp_adj[SP_ADJ_LAST];
|
||||
#define ZREG_FCARG1x ZREG_X0
|
||||
#define ZREG_FCARG2x ZREG_X1
|
||||
|
||||
|.type EX, zend_execute_data, FP
|
||||
|.type OP, zend_op
|
||||
|.type ZVAL, zval
|
||||
|.actionlist dasm_actions
|
||||
|.globals zend_lb
|
||||
|.section code, cold_code, jmp_table
|
||||
|
||||
static void* dasm_labels[zend_lb_MAX];
|
||||
|
||||
#if ZTS
|
||||
static size_t tsrm_ls_cache_tcb_offset = 0;
|
||||
# ifdef __APPLE__
|
||||
struct TLVDescriptor {
|
||||
void* (*thunk)(struct TLVDescriptor*);
|
||||
uint64_t key;
|
||||
uint64_t offset;
|
||||
};
|
||||
typedef struct TLVDescriptor TLVDescriptor;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1)))
|
||||
|
||||
/* Encoding of immediate. TODO: shift mode may be supported in the near future. */
|
||||
#define MAX_IMM12 0xfff // maximum value for imm12
|
||||
@@ -146,75 +172,8 @@ static bool arm64_may_use_adrp(const void *addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "Zend/zend_cpuinfo.h"
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
# include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
/* The generated code may contain tautological comparisons, ignore them. */
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wtautological-compare"
|
||||
# pragma clang diagnostic ignored "-Wstring-compare"
|
||||
#endif
|
||||
|
||||
const char* zend_reg_name[] = {
|
||||
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
|
||||
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
|
||||
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
|
||||
"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
|
||||
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
|
||||
"d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
|
||||
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
|
||||
"d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
|
||||
};
|
||||
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
# define GCC_GLOBAL_REGS 1
|
||||
#else
|
||||
# define GCC_GLOBAL_REGS 0
|
||||
#endif
|
||||
|
||||
# define ZREG_FCARG1x ZREG_X0
|
||||
# define ZREG_FCARG2x ZREG_X1
|
||||
|
||||
#if ZTS
|
||||
static size_t tsrm_ls_cache_tcb_offset = 0;
|
||||
# ifdef __APPLE__
|
||||
struct TLVDescriptor {
|
||||
void* (*thunk)(struct TLVDescriptor*);
|
||||
uint64_t key;
|
||||
uint64_t offset;
|
||||
};
|
||||
typedef struct TLVDescriptor TLVDescriptor;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* By default avoid JITing inline handlers if it does not seem profitable due to lack of
|
||||
* type information. Disabling this option allows testing some JIT handlers in the
|
||||
* presence of try/catch blocks, which prevent SSA construction. */
|
||||
#ifndef PROFITABILITY_CHECKS
|
||||
# define PROFITABILITY_CHECKS 1
|
||||
#endif
|
||||
|
||||
|.type EX, zend_execute_data, FP
|
||||
|.type OP, zend_op
|
||||
|.type ZVAL, zval
|
||||
|
||||
|.actionlist dasm_actions
|
||||
|
||||
|.globals zend_lb
|
||||
static void* dasm_labels[zend_lb_MAX];
|
||||
|
||||
|.section code, cold_code, jmp_table
|
||||
|
||||
#define IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1)))
|
||||
|
||||
#define BP_JIT_IS 6
|
||||
|
||||
/* Determine whether an immediate value can be encoded as the immediate operand of logical instructions. */
|
||||
static bool logical_immediate_p (uint64_t value, uint32_t reg_size)
|
||||
static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
|
||||
{
|
||||
/* fast path: power of two */
|
||||
if (value > 0 && !(value & (value - 1))) {
|
||||
@@ -537,13 +496,6 @@ static bool logical_immediate_p (uint64_t value, uint32_t reg_size)
|
||||
| str tmp_reg, mem
|
||||
|.endmacro
|
||||
|
||||
/* Compare address 'addr' with the value in memory 'mem'. */
|
||||
|.macro ADDR_CMP, mem, addr, tmp_reg1, tmp_reg2
|
||||
| LOAD_ADDR tmp_reg1, addr
|
||||
| ldr tmp_reg2, mem
|
||||
| cmp tmp_reg2, tmp_reg1
|
||||
|.endmacro
|
||||
|
||||
/* Store a register value 'reg' into memory 'addr'.
|
||||
* For ZTS mode, base register with unsigned offset variant is used,
|
||||
* and 8-bit/32-bit/64-bit registers to be transferred are distinguished. */
|
||||
@@ -651,14 +603,14 @@ static bool logical_immediate_p (uint64_t value, uint32_t reg_size)
|
||||
* result is stored back to memory 'addr'. Operand 'op' can be either a register value or an immediate value.
|
||||
* Currently, only add instruction is used as 'op_ins'.
|
||||
* Note: It should be guaranteed that the immediate value can be encoded for 'op_ins'. */
|
||||
|.macro MEM_LOAD_OP_STORE, op_ins, ldr_ins, str_ins, op, addr, tmp_reg1, tmp_reg2
|
||||
|.macro MEM_UPDATE, op_ins, ldr_ins, str_ins, op, addr, tmp_reg1, tmp_reg2
|
||||
| LOAD_ADDR tmp_reg2, addr
|
||||
| ldr_ins, tmp_reg1, [tmp_reg2]
|
||||
| op_ins tmp_reg1, tmp_reg1, op
|
||||
| str_ins tmp_reg1, [tmp_reg2]
|
||||
|.endmacro
|
||||
|
||||
|.macro MEM_LOAD_OP_STORE_ZTS, op_ins, ldr_ins, str_ins, op, struct, field, tmp_reg1, tmp_reg2
|
||||
|.macro MEM_UPDATE_ZTS, op_ins, ldr_ins, str_ins, op, struct, field, tmp_reg1, tmp_reg2
|
||||
| .if ZTS
|
||||
| LOAD_TSRM_CACHE TMP3
|
||||
|| if (((uintptr_t)(struct.._offset+offsetof(zend_..struct, field))) > LDR_STR_PIMM64) {
|
||||
@@ -672,22 +624,10 @@ static bool logical_immediate_p (uint64_t value, uint32_t reg_size)
|
||||
| str_ins tmp_reg2, [TMP3, #(struct.._offset+offsetof(zend_..struct, field))]
|
||||
|| }
|
||||
| .else
|
||||
| MEM_LOAD_OP_STORE op_ins, ldr_ins, str_ins, op, &struct.field, tmp_reg1, tmp_reg2
|
||||
| MEM_UPDATE op_ins, ldr_ins, str_ins, op, &struct.field, tmp_reg1, tmp_reg2
|
||||
| .endif
|
||||
|.endmacro
|
||||
|
||||
|.macro LOAD_BASE_ADDR, reg, base, offset
|
||||
|| if (offset) {
|
||||
| ADD_SUB_64_WITH_CONST_32 add, reg, Rx(base), offset, reg
|
||||
|| } else {
|
||||
|| if (base == ZREG_RSP) {
|
||||
| mov reg, sp
|
||||
|| } else {
|
||||
| mov reg, Rx(base)
|
||||
|| }
|
||||
|| }
|
||||
|.endmacro
|
||||
|
||||
|.macro EXT_CALL, func, tmp_reg
|
||||
|| if (arm64_may_use_b(func)) {
|
||||
| bl &func
|
||||
@@ -806,7 +746,15 @@ static bool logical_immediate_p (uint64_t value, uint32_t reg_size)
|
||||
|| if (Z_MODE(addr) == IS_CONST_ZVAL) {
|
||||
| LOAD_ADDR reg, Z_ZV(addr)
|
||||
|| } else if (Z_MODE(addr) == IS_MEM_ZVAL) {
|
||||
| LOAD_BASE_ADDR reg, Z_REG(addr), Z_OFFSET(addr)
|
||||
|| if (Z_OFFSET(addr)) {
|
||||
| ADD_SUB_64_WITH_CONST_32 add, reg, Rx(Z_REG(addr)), Z_OFFSET(addr), reg
|
||||
|| } else {
|
||||
|| if (Z_REG(addr) == ZREG_RSP) {
|
||||
| mov reg, sp
|
||||
|| } else {
|
||||
| mov reg, Rx(Z_REG(addr))
|
||||
|| }
|
||||
|| }
|
||||
|| } else {
|
||||
|| ZEND_UNREACHABLE();
|
||||
|| }
|
||||
@@ -2586,18 +2534,6 @@ static int zend_jit_context_threaded_call_stub(dasm_State **Dst)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int zend_jit_assign_to_variable(dasm_State **Dst,
|
||||
const zend_op *opline,
|
||||
zend_jit_addr var_use_addr,
|
||||
zend_jit_addr var_addr,
|
||||
uint32_t var_info,
|
||||
uint32_t var_def_info,
|
||||
zend_uchar val_type,
|
||||
zend_jit_addr val_addr,
|
||||
uint32_t val_info,
|
||||
zend_jit_addr res_addr,
|
||||
bool check_exception);
|
||||
|
||||
static int zend_jit_assign_const_stub(dasm_State **Dst)
|
||||
{
|
||||
zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1x, 0);
|
||||
@@ -2743,12 +2679,6 @@ static const zend_jit_stub zend_jit_stubs[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#if ZTS && defined(ZEND_WIN32)
|
||||
extern uint32_t _tls_index;
|
||||
extern char *_tls_start;
|
||||
extern char *_tls_end;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GDB
|
||||
# if 0
|
||||
typedef struct _Unwind_Context _Unwind_Context;
|
||||
@@ -6285,85 +6215,6 @@ static int zend_jit_assign_op(dasm_State **Dst, const zend_op *opline, uint32_t
|
||||
return result;
|
||||
}
|
||||
|
||||
static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
|
||||
zend_ssa_range *op1_range,
|
||||
zend_jit_addr op1_addr,
|
||||
zend_ssa_range *op2_range,
|
||||
zend_jit_addr op2_addr,
|
||||
bool *result)
|
||||
{
|
||||
zend_long op1_min;
|
||||
zend_long op1_max;
|
||||
zend_long op2_min;
|
||||
zend_long op2_max;
|
||||
|
||||
if (op1_range) {
|
||||
op1_min = op1_range->min;
|
||||
op1_max = op1_range->max;
|
||||
} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
|
||||
ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG);
|
||||
op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (op2_range) {
|
||||
op2_min = op2_range->min;
|
||||
op2_max = op2_range->max;
|
||||
} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
|
||||
ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG);
|
||||
op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_IS_EQUAL:
|
||||
case ZEND_IS_IDENTICAL:
|
||||
case ZEND_CASE:
|
||||
case ZEND_CASE_STRICT:
|
||||
if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
} else if (op1_max < op2_min || op1_min > op2_max) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_IS_NOT_EQUAL:
|
||||
case ZEND_IS_NOT_IDENTICAL:
|
||||
if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
} else if (op1_max < op2_min || op1_min > op2_max) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_IS_SMALLER:
|
||||
if (op1_max < op2_min) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
} else if (op1_min >= op2_max) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_IS_SMALLER_OR_EQUAL:
|
||||
if (op1_max <= op2_min) {
|
||||
*result = 1;
|
||||
return 1;
|
||||
} else if (op1_min > op2_max) {
|
||||
*result = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
ZEND_UNREACHABLE();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_cmp_long_long(dasm_State **Dst,
|
||||
const zend_op *opline,
|
||||
zend_ssa_range *op1_range,
|
||||
@@ -8447,13 +8298,13 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
|
||||
|
||||
if (func) {
|
||||
|| if (used_stack <= ADD_SUB_IMM) {
|
||||
| MEM_LOAD_OP_STORE_ZTS add, ldr, str, #used_stack, executor_globals, vm_stack_top, REG2, TMP1
|
||||
| MEM_UPDATE_ZTS add, ldr, str, #used_stack, executor_globals, vm_stack_top, REG2, TMP1
|
||||
|| } else {
|
||||
| LOAD_32BIT_VAL TMP1w, used_stack
|
||||
| MEM_LOAD_OP_STORE_ZTS add, ldr, str, TMP1, executor_globals, vm_stack_top, REG2, TMP2
|
||||
| MEM_UPDATE_ZTS add, ldr, str, TMP1, executor_globals, vm_stack_top, REG2, TMP2
|
||||
|| }
|
||||
} else {
|
||||
| MEM_LOAD_OP_STORE_ZTS add, ldr, str, FCARG1x, executor_globals, vm_stack_top, REG2, TMP1
|
||||
| MEM_UPDATE_ZTS add, ldr, str, FCARG1x, executor_globals, vm_stack_top, REG2, TMP1
|
||||
}
|
||||
| // zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
|
||||
if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) {
|
||||
@@ -8560,186 +8411,6 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, const zend_op *opline, zend_jit_trace_rec *trace)
|
||||
{
|
||||
int skip;
|
||||
|
||||
if (trace) {
|
||||
zend_jit_trace_rec *p = trace;
|
||||
|
||||
ssa_op++;
|
||||
while (1) {
|
||||
if (p->op == ZEND_JIT_TRACE_VM) {
|
||||
switch (p->opline->opcode) {
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_JMP:
|
||||
case ZEND_JMPZNZ:
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_JMP_NULL:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
return 1;
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_FCALL:
|
||||
return 0;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
/* skip */
|
||||
break;
|
||||
default:
|
||||
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
ssa_op += zend_jit_trace_op_len(opline);
|
||||
} else if (p->op == ZEND_JIT_TRACE_ENTER ||
|
||||
p->op == ZEND_JIT_TRACE_BACK ||
|
||||
p->op == ZEND_JIT_TRACE_END) {
|
||||
return 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!call_info) {
|
||||
const zend_op *end = op_array->opcodes + op_array->last;
|
||||
|
||||
opline++;
|
||||
ssa_op++;
|
||||
skip = 1;
|
||||
while (opline != end) {
|
||||
if (!skip) {
|
||||
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
skip = 0;
|
||||
break;
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_JMP:
|
||||
case ZEND_JMPZNZ:
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_JMP_NULL:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
return 1;
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_FCALL:
|
||||
end = opline;
|
||||
if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
|
||||
/* INIT_FCALL and DO_FCALL in different BasicBlocks */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
opline++;
|
||||
ssa_op++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
const zend_op *end = call_info->caller_call_opline;
|
||||
|
||||
if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
|
||||
/* INIT_FCALL and DO_FCALL in different BasicBlocks */
|
||||
return 1;
|
||||
}
|
||||
|
||||
opline++;
|
||||
ssa_op++;
|
||||
skip = 1;
|
||||
while (opline != end) {
|
||||
if (skip) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
skip = 0;
|
||||
break;
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_UNPACK:
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
opline++;
|
||||
ssa_op++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zend_function *func, const zend_op *to_opline)
|
||||
{
|
||||
int32_t exit_point;
|
||||
@@ -9225,33 +8896,6 @@ static int zend_jit_init_closure_call(dasm_State **Dst,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ssa, const zend_call_info *call_info)
|
||||
{
|
||||
uint32_t num_args = 0;
|
||||
zend_function *func = call_info->callee_func;
|
||||
|
||||
/* It's okay to handle prototypes here, because they can only increase the accepted arguments.
|
||||
* Anything legal for the parent method is also legal for the parent method. */
|
||||
while (num_args < call_info->num_args) {
|
||||
zend_arg_info *arg_info = func->op_array.arg_info + num_args;
|
||||
|
||||
if (ZEND_TYPE_IS_SET(arg_info->type)) {
|
||||
if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) {
|
||||
zend_op *opline = call_info->arg_info[num_args].opline;
|
||||
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
|
||||
uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
|
||||
if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
num_args++;
|
||||
}
|
||||
return num_args;
|
||||
}
|
||||
|
||||
static 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)
|
||||
{
|
||||
zend_func_info *info = ZEND_FUNC_INFO(op_array);
|
||||
@@ -10724,44 +10368,6 @@ static int zend_jit_type_check(dasm_State **Dst, const zend_op *opline, uint32_t
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t zend_ssa_cv_info(const zend_op_array *op_array, zend_ssa *ssa, uint32_t var)
|
||||
{
|
||||
uint32_t j, info;
|
||||
|
||||
if (ssa->vars && ssa->var_info) {
|
||||
info = ssa->var_info[var].type;
|
||||
for (j = op_array->last_var; j < ssa->vars_count; j++) {
|
||||
if (ssa->vars[j].var == var) {
|
||||
info |= ssa->var_info[j].type;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF |
|
||||
MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
|
||||
}
|
||||
|
||||
#ifdef ZEND_JIT_USE_RC_INFERENCE
|
||||
/* Refcount may be increased by RETURN opcode */
|
||||
if ((info & MAY_BE_RC1) && !(info & MAY_BE_RCN)) {
|
||||
for (j = 0; j < ssa->cfg.blocks_count; j++) {
|
||||
if ((ssa->cfg.blocks[j].flags & ZEND_BB_REACHABLE) &&
|
||||
ssa->cfg.blocks[j].len > 0) {
|
||||
const zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].start + ssa->cfg.blocks[j].len - 1;
|
||||
|
||||
if (opline->opcode == ZEND_RETURN) {
|
||||
if (opline->op1_type == IS_CV && opline->op1.var == EX_NUM_TO_VAR(var)) {
|
||||
info |= MAY_BE_RCN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static int zend_jit_leave_frame(dasm_State **Dst)
|
||||
{
|
||||
| // EG(current_execute_data) = EX(prev_execute_data);
|
||||
@@ -11199,43 +10805,6 @@ static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, ze
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool zend_jit_may_avoid_refcounting(const zend_op *opline)
|
||||
{
|
||||
switch (opline->opcode) {
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
if (!JIT_G(current_frame) ||
|
||||
!JIT_G(current_frame)->call->func ||
|
||||
!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
|
||||
return 0;
|
||||
}
|
||||
/* break missing intentionally */
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
if (opline->op2_type == IS_CONST
|
||||
&& Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING
|
||||
&& Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] != '\0') {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
if (!JIT_G(current_frame) ||
|
||||
!JIT_G(current_frame)->call->func ||
|
||||
!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
|
||||
return 0;
|
||||
}
|
||||
/* break missing intentionally */
|
||||
case ZEND_FETCH_DIM_R:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
return 1;
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
if (!(opline->extended_value & ZEND_ISEMPTY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_fetch_dim_read(dasm_State **Dst,
|
||||
const zend_op *opline,
|
||||
zend_ssa *ssa,
|
||||
@@ -12175,89 +11744,6 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zen
|
||||
return 1;
|
||||
}
|
||||
|
||||
static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
|
||||
{
|
||||
zend_property_info *info = NULL;
|
||||
|
||||
if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) ||
|
||||
!ce ||
|
||||
!(ce->ce_flags & ZEND_ACC_LINKED) ||
|
||||
(ce->ce_flags & ZEND_ACC_TRAIT) ||
|
||||
ce->create_object) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
if (ce->info.user.filename != filename) {
|
||||
/* class declaration might be changed independently */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ce->parent) {
|
||||
zend_class_entry *parent = ce->parent;
|
||||
|
||||
do {
|
||||
if (parent->type == ZEND_INTERNAL_CLASS) {
|
||||
break;
|
||||
} else if (parent->info.user.filename != filename) {
|
||||
/* some of parents class declarations might be changed independently */
|
||||
/* TODO: this check may be not enough, because even
|
||||
* in the same it's possible to conditionally define
|
||||
* few classes with the same name, and "parent" may
|
||||
* change from request to request.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
parent = parent->parent;
|
||||
} while (parent);
|
||||
}
|
||||
}
|
||||
|
||||
info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
|
||||
if (info == NULL ||
|
||||
!IS_VALID_PROPERTY_OFFSET(info->offset) ||
|
||||
(info->flags & ZEND_ACC_STATIC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(info->flags & ZEND_ACC_PUBLIC) &&
|
||||
(!on_this || info->ce != ce)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
|
||||
{
|
||||
zend_property_info *info;
|
||||
|
||||
if (!ce || (ce->ce_flags & ZEND_ACC_TRAIT)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
if (ce->info.user.filename != filename) {
|
||||
/* class declaration might be changed independently */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
|
||||
if (info == NULL ||
|
||||
!IS_VALID_PROPERTY_OFFSET(info->offset) ||
|
||||
(info->flags & ZEND_ACC_STATIC)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(info->flags & ZEND_ACC_PUBLIC) &&
|
||||
(!on_this || info->ce != ce)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_class_guard(dasm_State **Dst, const zend_op *opline, zend_class_entry *ce)
|
||||
{
|
||||
int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
|
||||
@@ -15329,10 +14815,6 @@ static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int
|
||||
return n;
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
|
||||
+230
-723
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user