mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Tracing JIT (it doesn't support register allocation yet)
Use opcache.jit=1255 to swith it on (the third digit 5 really matters) Use opcache.jit_debug=0xff001 to see how it works and what code it generates
This commit is contained in:
parent
d15012d5e8
commit
4bf2d09ede
17 changed files with 7722 additions and 1183 deletions
|
@ -20,6 +20,236 @@
|
|||
#include "zend_compile.h"
|
||||
#include "zend_dfg.h"
|
||||
|
||||
static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
|
||||
{
|
||||
uint32_t var_num;
|
||||
const zend_op *next;
|
||||
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0
|
||||
&& opline->opcode != ZEND_FE_FETCH_R
|
||||
&& opline->opcode != ZEND_FE_FETCH_RW)
|
||||
|| (opline->op2_type == IS_CV)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op2.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
|
||||
&& opline->result_type == IS_CV
|
||||
&& opline->opcode != ZEND_RECV) {
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
add_op1_def:
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op1.var));
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_REF:
|
||||
if (opline->op2_type == IS_CV) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
#if 0
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_POST_DEC:
|
||||
case ZEND_BIND_GLOBAL:
|
||||
case ZEND_BIND_STATIC:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_MAKE_REF:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_CAST:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_FE_RESET_R:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_UNPACK:
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
/* break missing intentionally */
|
||||
case ZEND_INIT_ARRAY:
|
||||
if (((build_flags & ZEND_SSA_RC_INFERENCE)
|
||||
|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
|
||||
&& opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_YIELD:
|
||||
if (opline->op1_type == IS_CV
|
||||
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
|
||||
|| (build_flags & ZEND_SSA_RC_INFERENCE))) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_UNSET_CV:
|
||||
goto add_op1_def;
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
#if 0
|
||||
/* This special case was handled above the switch */
|
||||
if (opline->op2_type != IS_CV) {
|
||||
op2_use = -1; /* not used */
|
||||
}
|
||||
#endif
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
break;
|
||||
case ZEND_BIND_LEXICAL:
|
||||
if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->result.var));
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
|
||||
{
|
||||
_zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
|
||||
{
|
||||
int set_size;
|
||||
|
@ -27,7 +257,6 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg
|
|||
int blocks_count = cfg->blocks_count;
|
||||
zend_bitset tmp, def, use, in, out;
|
||||
int k;
|
||||
uint32_t var_num;
|
||||
int j;
|
||||
|
||||
set_size = dfg->size;
|
||||
|
@ -40,162 +269,19 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg
|
|||
/* Collect "def" and "use" sets */
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
zend_op *opline, *end;
|
||||
zend_bitset b_use, b_def;
|
||||
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
opline = op_array->opcodes + blocks[j].start;
|
||||
end = opline + blocks[j].len;
|
||||
b_use = DFG_BITSET(use, set_size, j);
|
||||
b_def = DFG_BITSET(def, set_size, j);
|
||||
for (; opline < end; opline++) {
|
||||
if (opline->opcode != ZEND_OP_DATA) {
|
||||
zend_op *next = opline + 1;
|
||||
if (next < end && next->opcode == ZEND_OP_DATA) {
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (next->op1_type == IS_CV && (opline->opcode == ZEND_ASSIGN_OBJ_REF
|
||||
|| opline->opcode == ZEND_ASSIGN_STATIC_PROP_REF)) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
DFG_SET(def, set_size, j, var_num);
|
||||
} else {
|
||||
if (!DFG_ISSET(def, set_size, j, var_num)) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op2.var);
|
||||
if (!DFG_ISSET(def, set_size, j, var_num)) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op1.var);
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
case ZEND_INIT_ARRAY:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE)
|
||||
|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
|
||||
goto op1_def;
|
||||
}
|
||||
goto op1_use;
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_CAST:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
if (build_flags & ZEND_SSA_RC_INFERENCE) {
|
||||
goto op1_def;
|
||||
}
|
||||
goto op1_use;
|
||||
case ZEND_YIELD:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE)
|
||||
|| (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
|
||||
goto op1_def;
|
||||
}
|
||||
goto op1_use;
|
||||
case ZEND_UNSET_CV:
|
||||
case ZEND_ASSIGN:
|
||||
case ZEND_ASSIGN_REF:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_BIND_GLOBAL:
|
||||
case ZEND_BIND_STATIC:
|
||||
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:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_POST_DEC:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
op1_def:
|
||||
/* `def` always come along with dtor or separation,
|
||||
* thus the origin var info might be also `use`d in the feature(CG) */
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
DFG_SET(def, set_size, j, var_num);
|
||||
break;
|
||||
default:
|
||||
op1_use:
|
||||
if (!DFG_ISSET(def, set_size, j, var_num)) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
}
|
||||
}
|
||||
} else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op1.var);
|
||||
if (!DFG_ISSET(def, set_size, j, var_num)) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
}
|
||||
if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
|
||||
DFG_SET(def, set_size, j, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op2_type == IS_CV) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op2.var);
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if (build_flags & ZEND_SSA_RC_INFERENCE) {
|
||||
goto op2_def;
|
||||
}
|
||||
goto op2_use;
|
||||
case ZEND_BIND_LEXICAL:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) || (opline->extended_value & ZEND_BIND_REF)) {
|
||||
goto op2_def;
|
||||
}
|
||||
goto op2_use;
|
||||
case ZEND_ASSIGN_REF:
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
op2_def:
|
||||
// FIXME: include into "use" too ...?
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
DFG_SET(def, set_size, j, var_num);
|
||||
break;
|
||||
default:
|
||||
op2_use:
|
||||
if (!DFG_ISSET(def, set_size, j, var_num)) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op2.var);
|
||||
if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
|
||||
DFG_SET(def, set_size, j, var_num);
|
||||
} else {
|
||||
if (!DFG_ISSET(def, set_size, j, var_num)) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
|
||||
&& opline->result_type == IS_CV) {
|
||||
DFG_SET(use, set_size, j, var_num);
|
||||
}
|
||||
DFG_SET(def, set_size, j, var_num);
|
||||
}
|
||||
_zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ typedef struct _zend_dfg {
|
|||
BEGIN_EXTERN_C()
|
||||
|
||||
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
|
||||
void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
|
|
|
@ -169,6 +169,9 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst
|
|||
int first = 1;
|
||||
|
||||
fprintf(stderr, " [");
|
||||
if (info & MAY_BE_GUARD) {
|
||||
fprintf(stderr, "!");
|
||||
}
|
||||
if (info & MAY_BE_UNDEF) {
|
||||
if (first) first = 0; else fprintf(stderr, ", ");
|
||||
fprintf(stderr, "undef");
|
||||
|
@ -329,7 +332,7 @@ static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num, uint32_
|
|||
dump_flags);
|
||||
}
|
||||
|
||||
static void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags)
|
||||
void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags)
|
||||
{
|
||||
if (ssa_var_num >= 0) {
|
||||
fprintf(stderr, "#%d.", ssa_var_num);
|
||||
|
@ -402,21 +405,16 @@ static void zend_dump_range_constraint(const zend_op_array *op_array, const zend
|
|||
}
|
||||
}
|
||||
|
||||
void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
|
||||
void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op)
|
||||
{
|
||||
const char *name = zend_get_opcode_name(opline->opcode);
|
||||
uint32_t flags = zend_get_opcode_flags(opline->opcode);
|
||||
uint32_t n = 0;
|
||||
const zend_ssa *ssa = NULL;
|
||||
|
||||
if (dump_flags & ZEND_DUMP_SSA) {
|
||||
ssa = (const zend_ssa*)data;
|
||||
}
|
||||
|
||||
if (!ssa || !ssa->ops || ssa->ops[opline - op_array->opcodes].result_use < 0) {
|
||||
if (!ssa_op || ssa_op->result_use < 0) {
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_def >= 0) {
|
||||
int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
|
||||
if (ssa_op && ssa_op->result_def >= 0) {
|
||||
int ssa_var_num = ssa_op->result_def;
|
||||
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
|
||||
} else {
|
||||
zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
|
||||
|
@ -582,12 +580,12 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
|
|||
if (opline->op1_type == IS_CONST) {
|
||||
zend_dump_const(CRT_CONSTANT(opline->op1));
|
||||
} else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
if (ssa && ssa->ops) {
|
||||
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_use;
|
||||
if (ssa_op) {
|
||||
int ssa_var_num = ssa_op->op1_use;
|
||||
if (ssa_var_num >= 0) {
|
||||
fprintf(stderr, " ");
|
||||
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
|
||||
} else if (ssa->ops[opline - op_array->opcodes].op1_def < 0) {
|
||||
} else if (ssa_op->op1_def < 0) {
|
||||
fprintf(stderr, " ");
|
||||
zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
|
||||
}
|
||||
|
@ -595,8 +593,8 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
|
|||
fprintf(stderr, " ");
|
||||
zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
|
||||
}
|
||||
if (ssa && ssa->ops) {
|
||||
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_def;
|
||||
if (ssa_op) {
|
||||
int ssa_var_num = ssa_op->op1_def;
|
||||
if (ssa_var_num >= 0) {
|
||||
fprintf(stderr, " -> ");
|
||||
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
|
||||
|
@ -643,12 +641,12 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
|
|||
zend_dump_const(op);
|
||||
}
|
||||
} else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
if (ssa && ssa->ops) {
|
||||
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_use;
|
||||
if (ssa_op) {
|
||||
int ssa_var_num = ssa_op->op2_use;
|
||||
if (ssa_var_num >= 0) {
|
||||
fprintf(stderr, " ");
|
||||
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
|
||||
} else if (ssa->ops[opline - op_array->opcodes].op2_def < 0) {
|
||||
} else if (ssa_op->op2_def < 0) {
|
||||
fprintf(stderr, " ");
|
||||
zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
|
@ -656,8 +654,8 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
|
|||
fprintf(stderr, " ");
|
||||
zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
if (ssa && ssa->ops) {
|
||||
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_def;
|
||||
if (ssa_op) {
|
||||
int ssa_var_num = ssa_op->op2_def;
|
||||
if (ssa_var_num >= 0) {
|
||||
fprintf(stderr, " -> ");
|
||||
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
|
||||
|
@ -697,10 +695,10 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
|
|||
} else if (opline->result_type & IS_SMART_BRANCH_JMPNZ) {
|
||||
fprintf(stderr, " jmpnz");
|
||||
#endif
|
||||
} else if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_use >= 0) {
|
||||
} else if (ssa_op && ssa_op->result_use >= 0) {
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
if (ssa && ssa->ops) {
|
||||
int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_use;
|
||||
if (ssa_op) {
|
||||
int ssa_var_num = ssa_op->result_use;
|
||||
if (ssa_var_num >= 0) {
|
||||
fprintf(stderr, " ");
|
||||
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
|
||||
|
@ -709,8 +707,8 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
|
|||
fprintf(stderr, " ");
|
||||
zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
|
||||
}
|
||||
if (ssa && ssa->ops) {
|
||||
int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
|
||||
if (ssa_op) {
|
||||
int ssa_var_num = ssa_op->result_def;
|
||||
if (ssa_var_num >= 0) {
|
||||
fprintf(stderr, " -> ");
|
||||
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
|
||||
|
@ -723,6 +721,8 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
|
|||
static void zend_dump_op_line(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
|
||||
{
|
||||
int len = 0;
|
||||
const zend_ssa *ssa = NULL;
|
||||
zend_ssa_op *ssa_op = NULL;
|
||||
|
||||
if (dump_flags & ZEND_DUMP_NUMERIC_OPLINES) {
|
||||
len = fprintf(stderr, "%04u:", (uint32_t)(opline - op_array->opcodes));
|
||||
|
@ -731,7 +731,14 @@ static void zend_dump_op_line(const zend_op_array *op_array, const zend_basic_bl
|
|||
}
|
||||
fprintf(stderr, "%*c", 12-len, ' ');
|
||||
|
||||
zend_dump_op(op_array, b, opline, dump_flags, data);
|
||||
if (dump_flags & ZEND_DUMP_SSA) {
|
||||
ssa = (const zend_ssa*)data;
|
||||
if (ssa && ssa->ops) {
|
||||
ssa_op = &ssa->ops[opline - op_array->opcodes];
|
||||
}
|
||||
}
|
||||
|
||||
zend_dump_op(op_array, b, opline, dump_flags, ssa, ssa_op);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -32,12 +32,13 @@
|
|||
BEGIN_EXTERN_C()
|
||||
|
||||
void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
|
||||
void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data);
|
||||
void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op);
|
||||
void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg);
|
||||
void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg);
|
||||
void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa);
|
||||
void zend_dump_variables(const zend_op_array *op_array);
|
||||
void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags);
|
||||
void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags);
|
||||
void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
|
||||
void zend_dump_op_array_name(const zend_op_array *op_array);
|
||||
void zend_dump_const(const zval *zv);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -26,6 +26,7 @@
|
|||
/* Bitmask for type inference (zend_ssa_var_info.type) */
|
||||
#include "zend_type_info.h"
|
||||
|
||||
#define MAY_BE_GUARD (1<<28) /* needs type guard */
|
||||
#define MAY_BE_IN_REG (1<<29) /* value allocated in CPU register */
|
||||
|
||||
//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
|
||||
|
@ -37,23 +38,26 @@
|
|||
|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)
|
||||
|
||||
#define DEFINE_SSA_OP_HAS_RANGE(opN) \
|
||||
static zend_always_inline zend_bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
static zend_always_inline zend_bool _ssa_##opN##_has_range_ex(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \
|
||||
} else { \
|
||||
return (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->ops && \
|
||||
ssa->var_info && \
|
||||
ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
|
||||
ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range); \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
static zend_always_inline zend_bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
{ \
|
||||
return _ssa_##opN##_has_range_ex(op_array, ssa, opline, ssa->ops + (opline - op_array->opcodes)); \
|
||||
}
|
||||
|
||||
#define DEFINE_SSA_OP_MIN_RANGE(opN) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_min_range_ex(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
|
@ -67,17 +71,20 @@
|
|||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->ops && \
|
||||
ssa->var_info && \
|
||||
ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
|
||||
ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.min; \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.min; \
|
||||
} \
|
||||
return ZEND_LONG_MIN; \
|
||||
} \
|
||||
static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
{ \
|
||||
return _ssa_##opN##_min_range_ex(op_array, ssa, opline, ssa->ops + (opline - op_array->opcodes)); \
|
||||
}
|
||||
|
||||
#define DEFINE_SSA_OP_MAX_RANGE(opN) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_max_range_ex(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
|
@ -91,17 +98,20 @@
|
|||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->ops && \
|
||||
ssa->var_info && \
|
||||
ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
|
||||
ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.max; \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.max; \
|
||||
} \
|
||||
return ZEND_LONG_MAX; \
|
||||
} \
|
||||
static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
{ \
|
||||
return _ssa_##opN##_max_range_ex(op_array, ssa, opline, ssa->ops + (opline - op_array->opcodes)); \
|
||||
}
|
||||
|
||||
#define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \
|
||||
static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
static zend_always_inline char _ssa_##opN##_range_underflow_ex(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
|
@ -109,17 +119,20 @@
|
|||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->ops && \
|
||||
ssa->var_info && \
|
||||
ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
|
||||
ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.underflow; \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.underflow; \
|
||||
} \
|
||||
return 1; \
|
||||
} \
|
||||
static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
{ \
|
||||
return _ssa_##opN##_range_underflow_ex(op_array, ssa, opline, ssa->ops + (opline - op_array->opcodes)); \
|
||||
}
|
||||
|
||||
#define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \
|
||||
static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
static zend_always_inline char _ssa_##opN##_range_overflow_ex(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
|
@ -127,13 +140,16 @@
|
|||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->ops && \
|
||||
ssa->var_info && \
|
||||
ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
|
||||
ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.overflow; \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.overflow; \
|
||||
} \
|
||||
return 1; \
|
||||
} \
|
||||
static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
{ \
|
||||
return _ssa_##opN##_range_overflow_ex(op_array, ssa, opline, ssa->ops + (opline - op_array->opcodes)); \
|
||||
}
|
||||
|
||||
DEFINE_SSA_OP_HAS_RANGE(op1)
|
||||
|
@ -158,6 +174,17 @@ DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
|
|||
#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline))
|
||||
#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline))
|
||||
|
||||
#define OP1_HAS_RANGE_EX() (_ssa_op1_has_range_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_MIN_RANGE_EX() (_ssa_op1_min_range_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_MAX_RANGE_EX() (_ssa_op1_max_range_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_RANGE_UNDERFLOW_EX() (_ssa_op1_range_underflow_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_RANGE_OVERFLOW_EX() (_ssa_op1_range_overflow_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_HAS_RANGE_EX() (_ssa_op2_has_range_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_MIN_RANGE_EX() (_ssa_op2_min_range_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_MAX_RANGE_EX() (_ssa_op2_max_range_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_RANGE_UNDERFLOW_EX() (_ssa_op2_range_underflow_ex (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_RANGE_OVERFLOW_EX() (_ssa_op2_range_overflow_ex (op_array, ssa, opline, ssa_op))
|
||||
|
||||
static zend_always_inline uint32_t _const_op_type(const zval *zv) {
|
||||
if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
|
||||
return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
|
||||
|
@ -204,19 +231,27 @@ static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa
|
|||
}
|
||||
|
||||
#define DEFINE_SSA_OP_INFO(opN) \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_info_ex(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
return _const_op_type(CRT_CONSTANT(opline->opN)); \
|
||||
} else { \
|
||||
return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_use : -1); \
|
||||
return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \
|
||||
} \
|
||||
} \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
{ \
|
||||
return _ssa_##opN##_info_ex(op_array, ssa, opline, ssa->ops + (opline - op_array->opcodes)); \
|
||||
}
|
||||
|
||||
#define DEFINE_SSA_OP_DEF_INFO(opN) \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_def_info_ex(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \
|
||||
} \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
|
||||
{ \
|
||||
return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_def : -1); \
|
||||
return _ssa_##opN##_def_info_ex(op_array, ssa, opline, ssa->ops + (opline - op_array->opcodes)); \
|
||||
}
|
||||
|
||||
|
||||
|
@ -238,6 +273,17 @@ DEFINE_SSA_OP_DEF_INFO(result)
|
|||
#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1)))
|
||||
#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline))
|
||||
|
||||
#define OP1_INFO_EX() (_ssa_op1_info_ex(op_array, ssa, opline, ssa_op))
|
||||
#define OP2_INFO_EX() (_ssa_op2_info_ex(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DATA_INFO_EX() (_ssa_op1_info_ex(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define OP2_DATA_INFO_EX() (_ssa_op2_info_ex(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define RES_USE_INFO_EX() (_ssa_result_info_ex(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DEF_INFO_EX() (_ssa_op1_def_info_ex(op_array, ssa, opline, ssa_op))
|
||||
#define OP2_DEF_INFO_EX() (_ssa_op2_def_info_ex(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DATA_DEF_INFO_EX() (_ssa_op1_def_info_ex(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define OP2_DATA_DEF_INFO_EX() (_ssa_op2_def_info_ex(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define RES_INFO_EX() (_ssa_result_def_info_ex(op_array, ssa, opline, ssa_op))
|
||||
|
||||
static zend_always_inline zend_bool zend_add_will_overflow(zend_long a, zend_long b) {
|
||||
return (b > 0 && a > ZEND_LONG_MAX - b)
|
||||
|| (b < 0 && a < ZEND_LONG_MIN - b);
|
||||
|
@ -274,8 +320,17 @@ void zend_func_return_info(const zend_op_array *op_array,
|
|||
int widening,
|
||||
zend_ssa_var_info *ret);
|
||||
|
||||
int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
int zend_may_throw(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
|
||||
int zend_update_type_info(const zend_op_array *op_array,
|
||||
zend_ssa *ssa,
|
||||
const zend_script *script,
|
||||
zend_op *opline,
|
||||
zend_ssa_op *ssa_op,
|
||||
const zend_op **ssa_opcodes,
|
||||
zend_long optimization_level);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_INFERENCE_H */
|
||||
|
|
|
@ -537,6 +537,237 @@ static void place_essa_pis(
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */
|
||||
{
|
||||
const zend_op *next;
|
||||
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + opline->op1.var)
|
||||
}
|
||||
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + opline->op2.var)
|
||||
}
|
||||
if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
|
||||
&& opline->result_type == IS_CV
|
||||
&& opline->opcode != ZEND_RECV) {
|
||||
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + opline->result.var)
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
add_op1_def:
|
||||
ssa_ops[k].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op1.var)
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_REF:
|
||||
if (opline->op2_type == IS_CV) {
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
if (next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
#if 0
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
if (next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_POST_DEC:
|
||||
case ZEND_BIND_GLOBAL:
|
||||
case ZEND_BIND_STATIC:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_MAKE_REF:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_CAST:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_FE_RESET_R:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_UNPACK:
|
||||
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
|
||||
/* break missing intentionally */
|
||||
case ZEND_INIT_ARRAY:
|
||||
if (((build_flags & ZEND_SSA_RC_INFERENCE)
|
||||
|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
|
||||
&& opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_YIELD:
|
||||
if (opline->op1_type == IS_CV
|
||||
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
|
||||
|| (build_flags & ZEND_SSA_RC_INFERENCE))) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_UNSET_CV:
|
||||
goto add_op1_def;
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
if (opline->op2_type != IS_CV) {
|
||||
ssa_ops[k].op2_use = -1; /* not used */
|
||||
}
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
break;
|
||||
case ZEND_BIND_LEXICAL:
|
||||
if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k].result_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(op_array->last_var + opline->result.var)
|
||||
}
|
||||
|
||||
return ssa_vars_count;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */
|
||||
{
|
||||
return _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = ssa->cfg.blocks;
|
||||
|
@ -544,7 +775,7 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags,
|
|||
zend_ssa_op *ssa_ops = ssa->ops;
|
||||
int ssa_vars_count = ssa->vars_count;
|
||||
int i, j;
|
||||
zend_op *opline, *end, *next;
|
||||
zend_op *opline, *end;
|
||||
int *tmp = NULL;
|
||||
ALLOCA_FLAG(use_heap = 0);
|
||||
|
||||
|
@ -574,223 +805,7 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags,
|
|||
for (; opline < end; opline++) {
|
||||
uint32_t k = opline - op_array->opcodes;
|
||||
if (opline->opcode != ZEND_OP_DATA) {
|
||||
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + opline->op1.var)
|
||||
}
|
||||
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + opline->op2.var)
|
||||
}
|
||||
if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
|
||||
&& opline->result_type == IS_CV
|
||||
&& opline->opcode != ZEND_RECV) {
|
||||
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + opline->result.var)
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
add_op1_def:
|
||||
ssa_ops[k].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op1.var)
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_REF:
|
||||
if (opline->op2_type == IS_CV) {
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
if (next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
#if 0
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
if (next->op1_type == IS_CV) {
|
||||
ssa_ops[k + 1].op1_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(next->op1.var)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
|
||||
//USE_SSA_VAR(op_array->last_var + next->op1.var);
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_POST_DEC:
|
||||
case ZEND_BIND_GLOBAL:
|
||||
case ZEND_BIND_STATIC:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_MAKE_REF:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_CAST:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_FE_RESET_R:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_UNPACK:
|
||||
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
|
||||
/* break missing intentionally */
|
||||
case ZEND_INIT_ARRAY:
|
||||
if (((build_flags & ZEND_SSA_RC_INFERENCE)
|
||||
|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
|
||||
&& opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_YIELD:
|
||||
if (opline->op1_type == IS_CV
|
||||
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
|
||||
|| (build_flags & ZEND_SSA_RC_INFERENCE))) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_UNSET_CV:
|
||||
goto add_op1_def;
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
if (opline->op2_type != IS_CV) {
|
||||
ssa_ops[k].op2_use = -1; /* not used */
|
||||
}
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
break;
|
||||
case ZEND_BIND_LEXICAL:
|
||||
if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
|
||||
ssa_ops[k].op2_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(opline->op2.var)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
ssa_ops[k].result_def = ssa_vars_count;
|
||||
var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
|
||||
ssa_vars_count++;
|
||||
//NEW_SSA_VAR(op_array->last_var + opline->result.var)
|
||||
}
|
||||
ssa_vars_count = _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,6 +142,7 @@ typedef struct _zend_ssa {
|
|||
BEGIN_EXTERN_C()
|
||||
|
||||
int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa);
|
||||
int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var);
|
||||
int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
|
||||
|
||||
|
|
|
@ -13,5 +13,6 @@ $(builddir)/jit/zend_jit.lo: \
|
|||
$(srcdir)/jit/zend_jit_perf_dump.c \
|
||||
$(srcdir)/jit/zend_jit_oprofile.c \
|
||||
$(srcdir)/jit/zend_jit_vtune.c \
|
||||
$(srcdir)/jit/zend_jit_trace.c \
|
||||
$(srcdir)/jit/zend_elf.c
|
||||
|
||||
|
|
|
@ -13,4 +13,5 @@ $(BUILD_DIR)\opcache_jit\zend_jit.obj: \
|
|||
ext/opcache/jit/zend_jit_gdb.c \
|
||||
ext/opcache/jit/zend_jit_perf_dump.c \
|
||||
ext/opcache/jit/zend_jit_oprofile.c \
|
||||
ext/opcache/jit/zend_jit_trace.c \
|
||||
ext/opcache/jit/zend_jit_vtune.c
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "Zend/zend_constants.h"
|
||||
#include "zend_smart_str.h"
|
||||
#include "jit/zend_jit.h"
|
||||
#include "jit/zend_jit_internal.h"
|
||||
|
||||
#ifdef HAVE_JIT
|
||||
|
||||
|
@ -34,6 +33,14 @@
|
|||
#include "Optimizer/zend_call_graph.h"
|
||||
#include "Optimizer/zend_dump.h"
|
||||
|
||||
#include "jit/zend_jit_internal.h"
|
||||
|
||||
#ifdef ZTS
|
||||
int zend_jit_globals_id;
|
||||
#else
|
||||
zend_jit_globals jit_globals;
|
||||
#endif
|
||||
|
||||
//#define CONTEXT_THREADED_JIT
|
||||
#define ZEND_JIT_USE_RC_INFERENCE
|
||||
|
||||
|
@ -49,6 +56,7 @@
|
|||
|
||||
#define JIT_PREFIX "JIT$"
|
||||
#define JIT_STUB_PREFIX "JIT$$"
|
||||
#define TRACE_PREFIX "TRACE-"
|
||||
|
||||
#define DASM_M_GROW(ctx, t, p, sz, need) \
|
||||
do { \
|
||||
|
@ -97,11 +105,17 @@ static zend_long jit_bisect_pos = 0;
|
|||
static const void *zend_jit_runtime_jit_handler = NULL;
|
||||
static const void *zend_jit_profile_jit_handler = NULL;
|
||||
static const void *zend_jit_func_counter_handler = NULL;
|
||||
static const void *zend_jit_ret_counter_handler = NULL;
|
||||
static const void *zend_jit_loop_counter_handler = NULL;
|
||||
|
||||
static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
static void ZEND_FASTCALL zend_runtime_jit(void);
|
||||
|
||||
static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op *opline, zend_jit_trace_rec *trace);
|
||||
static uint32_t zend_jit_trace_get_exit_point(const zend_op *from_opline, const zend_op *to_opline, zend_jit_trace_rec *trace);
|
||||
static const void *zend_jit_trace_get_exit_addr(uint32_t n);
|
||||
static void zend_jit_trace_add_code(const void *start, uint32_t size);
|
||||
|
||||
static zend_bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use)
|
||||
{
|
||||
if (ssa->vars[var].phi_use_chain) {
|
||||
|
@ -133,22 +147,26 @@ static zend_bool zend_long_is_power_of_two(zend_long x)
|
|||
return (x > 0) && !(x & (x - 1));
|
||||
}
|
||||
|
||||
#define OP_RANGE(line, opN) \
|
||||
#define OP_RANGE_EX(ssa_op, opN) \
|
||||
(((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \
|
||||
ssa->ops && \
|
||||
ssa->var_info && \
|
||||
ssa->ops[line].opN##_use >= 0 && \
|
||||
ssa->var_info[ssa->ops[line].opN##_use].has_range) ? \
|
||||
&ssa->var_info[ssa->ops[line].opN##_use].range : NULL)
|
||||
(ssa_op)->opN##_use >= 0 && \
|
||||
ssa->var_info[(ssa_op)->opN##_use].has_range) ? \
|
||||
&ssa->var_info[(ssa_op)->opN##_use].range : NULL)
|
||||
|
||||
#define OP_RANGE(line, opN) \
|
||||
(ssa->var_info ? OP_RANGE_EX(ssa->ops + (line), opN) : NULL)
|
||||
|
||||
#define OP1_RANGE() OP_RANGE(opline - op_array->opcodes, op1)
|
||||
#define OP2_RANGE() OP_RANGE(opline - op_array->opcodes, op2)
|
||||
#define OP1_DATA_RANGE() OP_RANGE(opline - op_array->opcodes + 1, op1)
|
||||
|
||||
#define OP1_RANGE_EX() OP_RANGE_EX(ssa_op, op1)
|
||||
#define OP2_RANGE_EX() OP_RANGE_EX(ssa_op, op2)
|
||||
#define OP1_DATA_RANGE_EX() OP_RANGE_EX(ssa_op + 1, op1)
|
||||
|
||||
#include "dynasm/dasm_x86.h"
|
||||
#include "jit/zend_jit_x86.h"
|
||||
#include "jit/zend_jit_helpers.c"
|
||||
#include "jit/zend_jit_x86.c"
|
||||
#include "jit/zend_jit_disasm_x86.c"
|
||||
#ifndef _WIN32
|
||||
#include "jit/zend_jit_gdb.c"
|
||||
|
@ -159,6 +177,8 @@ static zend_bool zend_long_is_power_of_two(zend_long x)
|
|||
#endif
|
||||
#include "jit/zend_jit_vtune.c"
|
||||
|
||||
#include "jit/zend_jit_x86.c"
|
||||
|
||||
#if _WIN32
|
||||
# include <Windows.h>
|
||||
#else
|
||||
|
@ -168,8 +188,6 @@ static zend_bool zend_long_is_power_of_two(zend_long x)
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#define DASM_ALIGNMENT 16
|
||||
|
||||
ZEND_EXT_API void zend_jit_status(zval *ret)
|
||||
{
|
||||
zval stats;
|
||||
|
@ -220,7 +238,8 @@ static void *dasm_link_and_encode(dasm_State **dasm_state,
|
|||
zend_ssa *ssa,
|
||||
const zend_op *rt_opline,
|
||||
zend_lifetime_interval **ra,
|
||||
const char *name)
|
||||
const char *name,
|
||||
zend_bool is_trace)
|
||||
{
|
||||
size_t size;
|
||||
int ret;
|
||||
|
@ -289,6 +308,10 @@ static void *dasm_link_and_encode(dasm_State **dasm_state,
|
|||
entry = *dasm_ptr;
|
||||
*dasm_ptr = (void*)((char*)*dasm_ptr + ZEND_MM_ALIGNED_SIZE_EX(size, DASM_ALIGNMENT));
|
||||
|
||||
if (is_trace) {
|
||||
zend_jit_trace_add_code(entry, size);
|
||||
}
|
||||
|
||||
if (op_array && ssa) {
|
||||
int b;
|
||||
|
||||
|
@ -339,7 +362,7 @@ static void *dasm_link_and_encode(dasm_State **dasm_state,
|
|||
} else {
|
||||
if (ZCG(accel_directives).jit_debug & (ZEND_JIT_DEBUG_ASM_STUBS|ZEND_JIT_DEBUG_ASM)) {
|
||||
zend_jit_disasm_add_symbol(name, (uintptr_t)entry, size);
|
||||
if (ZCG(accel_directives).jit_debug & ZEND_JIT_DEBUG_ASM_STUBS) {
|
||||
if (is_trace || (ZCG(accel_directives).jit_debug & ZEND_JIT_DEBUG_ASM_STUBS) != 0) {
|
||||
zend_jit_disasm(
|
||||
name,
|
||||
(op_array && op_array->filename) ? ZSTR_VAL(op_array->filename) : NULL,
|
||||
|
@ -411,9 +434,8 @@ static void *dasm_link_and_encode(dasm_State **dasm_state,
|
|||
return entry;
|
||||
}
|
||||
|
||||
static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa)
|
||||
static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
|
||||
{
|
||||
uint32_t num;
|
||||
int res;
|
||||
|
||||
if (!ssa->ops || !ssa->var_info) {
|
||||
|
@ -422,21 +444,18 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
switch (opline->opcode) {
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_POST_INC:
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].op1_def;
|
||||
res = ssa_op->op1_def;
|
||||
return (res < 0 ||
|
||||
!ssa->var_info[res].has_range ||
|
||||
ssa->var_info[res].range.overflow);
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_DEC:
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].op1_def;
|
||||
res = ssa_op->op1_def;
|
||||
return (res < 0 ||
|
||||
!ssa->var_info[res].has_range ||
|
||||
ssa->var_info[res].range.underflow);
|
||||
case ZEND_ADD:
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].result_def;
|
||||
res = ssa_op->result_def;
|
||||
if (res < 0 ||
|
||||
!ssa->var_info[res].has_range) {
|
||||
return 1;
|
||||
|
@ -444,11 +463,11 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.underflow) {
|
||||
zend_long op1_min, op2_min;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_min = OP1_MIN_RANGE();
|
||||
op2_min = OP2_MIN_RANGE();
|
||||
op1_min = OP1_MIN_RANGE_EX();
|
||||
op2_min = OP2_MIN_RANGE_EX();
|
||||
if (zend_add_will_overflow(op1_min, op2_min)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -456,19 +475,18 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.overflow) {
|
||||
zend_long op1_max, op2_max;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_max = OP1_MAX_RANGE();
|
||||
op2_max = OP2_MAX_RANGE();
|
||||
op1_max = OP1_MAX_RANGE_EX();
|
||||
op2_max = OP2_MAX_RANGE_EX();
|
||||
if (zend_add_will_overflow(op1_max, op2_max)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case ZEND_SUB:
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].result_def;
|
||||
res = ssa_op->result_def;
|
||||
if (res < 0 ||
|
||||
!ssa->var_info[res].has_range) {
|
||||
return 1;
|
||||
|
@ -476,11 +494,11 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.underflow) {
|
||||
zend_long op1_min, op2_max;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_min = OP1_MIN_RANGE();
|
||||
op2_max = OP2_MAX_RANGE();
|
||||
op1_min = OP1_MIN_RANGE_EX();
|
||||
op2_max = OP2_MAX_RANGE_EX();
|
||||
if (zend_sub_will_overflow(op1_min, op2_max)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -488,27 +506,25 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.overflow) {
|
||||
zend_long op1_max, op2_min;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_max = OP1_MAX_RANGE();
|
||||
op2_min = OP2_MIN_RANGE();
|
||||
op1_max = OP1_MAX_RANGE_EX();
|
||||
op2_min = OP2_MIN_RANGE_EX();
|
||||
if (zend_sub_will_overflow(op1_max, op2_min)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case ZEND_MUL:
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].result_def;
|
||||
res = ssa_op->result_def;
|
||||
return (res < 0 ||
|
||||
!ssa->var_info[res].has_range ||
|
||||
ssa->var_info[res].range.underflow ||
|
||||
ssa->var_info[res].range.overflow);
|
||||
case ZEND_ASSIGN_OP:
|
||||
if (opline->extended_value == ZEND_ADD) {
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].op1_def;
|
||||
res = ssa_op->op1_def;
|
||||
if (res < 0 ||
|
||||
!ssa->var_info[res].has_range) {
|
||||
return 1;
|
||||
|
@ -516,11 +532,11 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.underflow) {
|
||||
zend_long op1_min, op2_min;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_min = OP1_MIN_RANGE();
|
||||
op2_min = OP2_MIN_RANGE();
|
||||
op1_min = OP1_MIN_RANGE_EX();
|
||||
op2_min = OP2_MIN_RANGE_EX();
|
||||
if (zend_add_will_overflow(op1_min, op2_min)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -528,19 +544,18 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.overflow) {
|
||||
zend_long op1_max, op2_max;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_max = OP1_MAX_RANGE();
|
||||
op2_max = OP2_MAX_RANGE();
|
||||
op1_max = OP1_MAX_RANGE_EX();
|
||||
op2_max = OP2_MAX_RANGE_EX();
|
||||
if (zend_add_will_overflow(op1_max, op2_max)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else if (opline->extended_value == ZEND_SUB) {
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].op1_def;
|
||||
res = ssa_op->op1_def;
|
||||
if (res < 0 ||
|
||||
!ssa->var_info[res].has_range) {
|
||||
return 1;
|
||||
|
@ -548,11 +563,11 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.underflow) {
|
||||
zend_long op1_min, op2_max;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_min = OP1_MIN_RANGE();
|
||||
op2_max = OP2_MAX_RANGE();
|
||||
op1_min = OP1_MIN_RANGE_EX();
|
||||
op2_max = OP2_MAX_RANGE_EX();
|
||||
if (zend_sub_will_overflow(op1_min, op2_max)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -560,19 +575,18 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
if (ssa->var_info[res].range.overflow) {
|
||||
zend_long op1_max, op2_min;
|
||||
|
||||
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
|
||||
if (!OP1_HAS_RANGE_EX() || !OP2_HAS_RANGE_EX()) {
|
||||
return 1;
|
||||
}
|
||||
op1_max = OP1_MAX_RANGE();
|
||||
op2_min = OP2_MIN_RANGE();
|
||||
op1_max = OP1_MAX_RANGE_EX();
|
||||
op2_min = OP2_MIN_RANGE_EX();
|
||||
if (zend_sub_will_overflow(op1_max, op2_min)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else if (opline->extended_value == ZEND_MUL) {
|
||||
num = opline - op_array->opcodes;
|
||||
res = ssa->ops[num].op1_def;
|
||||
res = ssa_op->op1_def;
|
||||
return (res < 0 ||
|
||||
!ssa->var_info[res].has_range ||
|
||||
ssa->var_info[res].range.underflow ||
|
||||
|
@ -583,6 +597,11 @@ static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_arra
|
|||
}
|
||||
}
|
||||
|
||||
static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa)
|
||||
{
|
||||
return zend_may_overflow_ex(opline, &ssa->ops[opline - op_array->opcodes], op_array, ssa);
|
||||
}
|
||||
|
||||
static int zend_jit_build_cfg(const zend_op_array *op_array, zend_cfg *cfg)
|
||||
{
|
||||
uint32_t flags;
|
||||
|
@ -2417,7 +2436,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
goto done;
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, call_level)) {
|
||||
if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, call_level, NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2466,7 +2485,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_FCALL:
|
||||
if (!zend_jit_do_fcall(&dasm_state, opline, op_array, ssa, call_level, b + 1)) {
|
||||
if (!zend_jit_do_fcall(&dasm_state, opline, op_array, ssa, call_level, b + 1, NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2503,7 +2522,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
OP2_INFO(), OP2_REG_ADDR(),
|
||||
res_addr,
|
||||
zend_may_throw(opline, op_array, ssa),
|
||||
smart_branch_opcode, target_label, target_label2)) {
|
||||
smart_branch_opcode, target_label, target_label2,
|
||||
NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2530,7 +2550,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
OP2_INFO(), OP2_REG_ADDR(),
|
||||
RES_REG_ADDR(),
|
||||
zend_may_throw(opline, op_array, ssa),
|
||||
smart_branch_opcode, target_label, target_label2)) {
|
||||
smart_branch_opcode, target_label, target_label2,
|
||||
NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2550,7 +2571,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
smart_branch_opcode = 0;
|
||||
target_label = target_label2 = (uint32_t)-1;
|
||||
}
|
||||
if (!zend_jit_defined(&dasm_state, opline, op_array, smart_branch_opcode, target_label, target_label2)) {
|
||||
if (!zend_jit_defined(&dasm_state, opline, op_array, smart_branch_opcode, target_label, target_label2, NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2574,7 +2595,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
smart_branch_opcode = 0;
|
||||
target_label = target_label2 = (uint32_t)-1;
|
||||
}
|
||||
if (!zend_jit_type_check(&dasm_state, opline, op_array, OP1_INFO(), smart_branch_opcode, target_label, target_label2)) {
|
||||
if (!zend_jit_type_check(&dasm_state, opline, op_array, OP1_INFO(), smart_branch_opcode, target_label, target_label2, NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2589,9 +2610,38 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
if (!zend_jit_tail_handler(&dasm_state, opline)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
} else if (!zend_jit_return(&dasm_state, opline, op_array, ssa,
|
||||
op1_info, OP1_REG_ADDR())) {
|
||||
goto jit_failure;
|
||||
} else {
|
||||
int j;
|
||||
|
||||
if (!zend_jit_return(&dasm_state, opline, op_array,
|
||||
op1_info, OP1_REG_ADDR())) {
|
||||
goto jit_failure;
|
||||
}
|
||||
if (jit_return_label >= 0) {
|
||||
if (!zend_jit_jmp(&dasm_state, jit_return_label)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
jit_return_label = ssa->cfg.blocks_count * 2;
|
||||
if (!zend_jit_label(&dasm_state, jit_return_label)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
if (!zend_jit_leave_frame(&dasm_state)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
for (j = 0 ; j < op_array->last_var; j++) {
|
||||
uint32_t info = zend_ssa_cv_info(opline, op_array, ssa, j);
|
||||
|
||||
if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
|
||||
if (!zend_jit_free_cv(&dasm_state, opline, op_array, info, j)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!zend_jit_leave_func(&dasm_state, opline, op_array, NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
}
|
||||
goto done;
|
||||
case ZEND_BOOL:
|
||||
|
@ -2599,7 +2649,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
if (!zend_jit_bool_jmpznz(&dasm_state, opline, op_array,
|
||||
OP1_INFO(), OP1_REG_ADDR(), RES_REG_ADDR(),
|
||||
-1, -1,
|
||||
zend_may_throw(opline, op_array, ssa))) {
|
||||
zend_may_throw(opline, op_array, ssa),
|
||||
opline->opcode, NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2625,7 +2676,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
if (!zend_jit_bool_jmpznz(&dasm_state, opline, op_array,
|
||||
OP1_INFO(), OP1_REG_ADDR(), res_addr,
|
||||
ssa->cfg.blocks[b].successors[0], ssa->cfg.blocks[b].successors[1],
|
||||
zend_may_throw(opline, op_array, ssa))) {
|
||||
zend_may_throw(opline, op_array, ssa),
|
||||
opline->opcode, NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2666,7 +2718,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
if (!zend_jit_isset_isempty_dim(&dasm_state, opline, op_array,
|
||||
OP1_INFO(), OP2_INFO(),
|
||||
zend_may_throw(opline, op_array, ssa),
|
||||
smart_branch_opcode, target_label, target_label2)) {
|
||||
smart_branch_opcode, target_label, target_label2,
|
||||
NULL)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
@ -2895,7 +2948,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
|||
zend_jit_call(&dasm_state, next_opline, b + 1);
|
||||
is_terminated = 1;
|
||||
} else {
|
||||
zend_jit_do_fcall(&dasm_state, next_opline, op_array, ssa, call_level, b + 1);
|
||||
zend_jit_do_fcall(&dasm_state, next_opline, op_array, ssa, call_level, b + 1, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2916,7 +2969,7 @@ done:
|
|||
}
|
||||
}
|
||||
|
||||
handler = dasm_link_and_encode(&dasm_state, op_array, ssa, rt_opline, ra, NULL);
|
||||
handler = dasm_link_and_encode(&dasm_state, op_array, ssa, rt_opline, ra, NULL, 0);
|
||||
if (!handler) {
|
||||
goto jit_failure;
|
||||
}
|
||||
|
@ -3072,17 +3125,16 @@ void zend_jit_check_funcs(HashTable *function_table, zend_bool is_method) {
|
|||
void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend_op *opline)
|
||||
{
|
||||
zend_op_array *op_array = &EX(func)->op_array;
|
||||
zend_jit_op_array_extension *jit_extension;
|
||||
zend_jit_op_array_hot_extension *jit_extension;
|
||||
uint32_t i;
|
||||
|
||||
zend_shared_alloc_lock();
|
||||
jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array);
|
||||
jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array);
|
||||
|
||||
if (jit_extension) {
|
||||
SHM_UNPROTECT();
|
||||
zend_jit_unprotect();
|
||||
|
||||
*(jit_extension->counter) = ZEND_JIT_HOT_COUNTER_INIT;
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
op_array->opcodes[i].handler = jit_extension->orig_handlers[i];
|
||||
}
|
||||
|
@ -3103,7 +3155,7 @@ void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend
|
|||
static int zend_jit_setup_hot_counters(zend_op_array *op_array)
|
||||
{
|
||||
zend_op *opline = op_array->opcodes;
|
||||
zend_jit_op_array_extension *jit_extension;
|
||||
zend_jit_op_array_hot_extension *jit_extension;
|
||||
zend_cfg cfg;
|
||||
uint32_t i;
|
||||
|
||||
|
@ -3114,7 +3166,7 @@ static int zend_jit_setup_hot_counters(zend_op_array *op_array)
|
|||
return FAILURE;
|
||||
}
|
||||
|
||||
jit_extension = (zend_jit_op_array_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_extension) + (op_array->last - 1) * sizeof(void*));
|
||||
jit_extension = (zend_jit_op_array_hot_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_hot_extension) + (op_array->last - 1) * sizeof(void*));
|
||||
jit_extension->counter = &zend_jit_hot_counters[zend_jit_op_array_hash(op_array) & (ZEND_HOT_COUNTERS_COUNT - 1)];
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
jit_extension->orig_handlers[i] = op_array->opcodes[i].handler;
|
||||
|
@ -3158,6 +3210,8 @@ static int zend_needs_manual_jit(const zend_op_array *op_array)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#include "jit/zend_jit_trace.c"
|
||||
|
||||
ZEND_EXT_API int zend_jit_op_array(zend_op_array *op_array, zend_script *script)
|
||||
{
|
||||
if (dasm_ptr == NULL) {
|
||||
|
@ -3195,6 +3249,8 @@ ZEND_EXT_API int zend_jit_op_array(zend_op_array *op_array, zend_script *script)
|
|||
return SUCCESS;
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_HOT_COUNTERS) {
|
||||
return zend_jit_setup_hot_counters(op_array);
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_HOT_TRACE) {
|
||||
return zend_jit_setup_hot_trace_counters(op_array);
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_SCRIPT_LOAD) {
|
||||
return zend_real_jit_func(op_array, script, NULL);
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_DOC_COMMENT) {
|
||||
|
@ -3230,7 +3286,8 @@ ZEND_EXT_API int zend_jit_script(zend_script *script)
|
|||
|
||||
if (zend_jit_trigger == ZEND_JIT_ON_FIRST_EXEC ||
|
||||
zend_jit_trigger == ZEND_JIT_ON_PROF_REQUEST ||
|
||||
zend_jit_trigger == ZEND_JIT_ON_HOT_COUNTERS) {
|
||||
zend_jit_trigger == ZEND_JIT_ON_HOT_COUNTERS ||
|
||||
zend_jit_trigger == ZEND_JIT_ON_HOT_TRACE) {
|
||||
for (i = 0; i < call_graph.op_arrays_count; i++) {
|
||||
ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
|
||||
if (zend_jit_op_array(call_graph.op_arrays[i], script) != SUCCESS) {
|
||||
|
@ -3381,7 +3438,7 @@ static int zend_jit_make_stubs(void)
|
|||
if (!zend_jit_stubs[i].stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
if (!dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, zend_jit_stubs[i].name)) {
|
||||
if (!dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, zend_jit_stubs[i].name, 0)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -3392,7 +3449,7 @@ static int zend_jit_make_stubs(void)
|
|||
if (!zend_jit_hybrid_runtime_jit_stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
zend_jit_runtime_jit_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_runtime_jit");
|
||||
zend_jit_runtime_jit_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_runtime_jit", 0);
|
||||
if (!zend_jit_runtime_jit_handler) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -3401,7 +3458,7 @@ static int zend_jit_make_stubs(void)
|
|||
if (!zend_jit_hybrid_profile_jit_stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
zend_jit_profile_jit_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_profile_jit");
|
||||
zend_jit_profile_jit_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_profile_jit", 0);
|
||||
if (!zend_jit_profile_jit_handler) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -3410,7 +3467,7 @@ static int zend_jit_make_stubs(void)
|
|||
if (!zend_jit_hybrid_func_counter_stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
zend_jit_func_counter_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_func_counter");
|
||||
zend_jit_func_counter_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_func_counter", 0);
|
||||
if (!zend_jit_func_counter_handler) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -3419,26 +3476,73 @@ static int zend_jit_make_stubs(void)
|
|||
if (!zend_jit_hybrid_loop_counter_stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
zend_jit_loop_counter_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_loop_counter");
|
||||
zend_jit_loop_counter_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_loop_counter", 0);
|
||||
if (!zend_jit_loop_counter_handler) {
|
||||
return 0;
|
||||
}
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_HOT_TRACE) {
|
||||
dasm_setup(&dasm_state, dasm_actions);
|
||||
if (!zend_jit_hybrid_func_trace_counter_stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
zend_jit_func_counter_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_func_counter", 0);
|
||||
if (!zend_jit_func_counter_handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dasm_setup(&dasm_state, dasm_actions);
|
||||
if (!zend_jit_hybrid_ret_trace_counter_stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
zend_jit_ret_counter_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_ret_counter", 0);
|
||||
if (!zend_jit_ret_counter_handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dasm_setup(&dasm_state, dasm_actions);
|
||||
if (!zend_jit_hybrid_loop_trace_counter_stub(&dasm_state)) {
|
||||
return 0;
|
||||
}
|
||||
zend_jit_loop_counter_handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, "JIT$$hybrid_loop_counter", 0);
|
||||
if (!zend_jit_loop_counter_handler) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zend_jit_runtime_jit_handler = (const void*)zend_runtime_jit;
|
||||
zend_jit_profile_jit_handler = (const void*)zend_jit_profile_helper;
|
||||
zend_jit_func_counter_handler = (const void*)zend_jit_func_counter_helper;
|
||||
zend_jit_loop_counter_handler = (const void*)zend_jit_loop_counter_helper;
|
||||
if (zend_jit_trigger == ZEND_JIT_ON_FIRST_EXEC) {
|
||||
zend_jit_runtime_jit_handler = (const void*)zend_runtime_jit;
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_PROF_REQUEST) {
|
||||
zend_jit_profile_jit_handler = (const void*)zend_jit_profile_helper;
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_HOT_COUNTERS) {
|
||||
zend_jit_func_counter_handler = (const void*)zend_jit_func_counter_helper;
|
||||
zend_jit_loop_counter_handler = (const void*)zend_jit_loop_counter_helper;
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_HOT_TRACE) {
|
||||
zend_jit_func_counter_handler = (const void*)zend_jit_func_trace_helper;
|
||||
zend_jit_ret_counter_handler = (const void*)zend_jit_ret_trace_helper;
|
||||
zend_jit_loop_counter_handler = (const void*)zend_jit_loop_trace_helper;
|
||||
}
|
||||
}
|
||||
|
||||
dasm_free(&dasm_state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void zend_jit_globals_ctor(zend_jit_globals *jit_globals)
|
||||
{
|
||||
memset(jit_globals, 0, sizeof(zend_jit_globals));
|
||||
zend_jit_trace_init_caches();
|
||||
}
|
||||
|
||||
ZEND_EXT_API int zend_jit_startup(zend_long jit, void *buf, size_t size, zend_bool reattached)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef ZTS
|
||||
zend_jit_globals_id = ts_allocate_id(&zend_jit_globals_id, sizeof(zend_jit_globals), (ts_allocate_ctor) zend_jit_globals_ctor, NULL);
|
||||
#else
|
||||
zend_jit_globals_ctor(&jit_globals);
|
||||
#endif
|
||||
|
||||
zend_jit_level = ZEND_JIT_LEVEL(jit);
|
||||
zend_jit_trigger = ZEND_JIT_TRIGGER(jit);
|
||||
zend_jit_reg_alloc = ZEND_JIT_REG_ALLOC(jit);
|
||||
|
@ -3549,6 +3653,12 @@ ZEND_EXT_API int zend_jit_startup(zend_long jit, void *buf, size_t size, zend_bo
|
|||
#endif
|
||||
}
|
||||
|
||||
if (zend_jit_trigger == ZEND_JIT_ON_HOT_TRACE) {
|
||||
if (zend_jit_trace_startup() != SUCCESS) {
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -3587,6 +3697,14 @@ ZEND_EXT_API void zend_jit_activate(void)
|
|||
for (i = 0; i < ZEND_HOT_COUNTERS_COUNT; i++) {
|
||||
zend_jit_hot_counters[i] = ZEND_JIT_HOT_COUNTER_INIT;
|
||||
}
|
||||
} else if (zend_jit_trigger == ZEND_JIT_ON_HOT_TRACE) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ZEND_HOT_COUNTERS_COUNT; i++) {
|
||||
zend_jit_hot_counters[i] = ZEND_JIT_TRACE_COUNTER_INIT;
|
||||
}
|
||||
|
||||
zend_jit_trace_reset_caches();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#define ZEND_JIT_ON_PROF_REQUEST 2 /* compile the most frequently caled on first request functions */
|
||||
#define ZEND_JIT_ON_HOT_COUNTERS 3 /* compile functions after N calls or loop iterations */
|
||||
#define ZEND_JIT_ON_DOC_COMMENT 4 /* compile functions with "@jit" tag in doc-comments */
|
||||
#define ZEND_JIT_ON_HOT_TRACE 5 /* trace functions after N calls or loop iterations */
|
||||
|
||||
#define ZEND_JIT_TRIGGER(n) (((n) / 10) % 10)
|
||||
|
||||
|
@ -75,6 +76,15 @@
|
|||
|
||||
#define ZEND_JIT_DEBUG_GDB (1<<8)
|
||||
|
||||
#define ZEND_JIT_DEBUG_TRACE_START (1<<12)
|
||||
#define ZEND_JIT_DEBUG_TRACE_STOP (1<<13)
|
||||
#define ZEND_JIT_DEBUG_TRACE_COMPILED (1<<14)
|
||||
#define ZEND_JIT_DEBUG_TRACE_EXIT (1<<15)
|
||||
#define ZEND_JIT_DEBUG_TRACE_ABORT (1<<16)
|
||||
#define ZEND_JIT_DEBUG_TRACE_BLACKLIST (1<<17)
|
||||
#define ZEND_JIT_DEBUG_TRACE_BYTECODE (1<<18)
|
||||
#define ZEND_JIT_DEBUG_TRACE_TSSA (1<<19)
|
||||
|
||||
ZEND_EXT_API int zend_jit_op_array(zend_op_array *op_array, zend_script *script);
|
||||
ZEND_EXT_API int zend_jit_script(zend_script *script);
|
||||
ZEND_EXT_API void zend_jit_unprotect(void);
|
||||
|
|
|
@ -137,6 +137,10 @@ static void zend_jit_disasm_add_symbol(const char *name,
|
|||
}
|
||||
} else {
|
||||
ZEND_ASSERT(sym->addr == node->addr);
|
||||
if (strcmp(name, node->name) == 0 && sym->end < node->end) {
|
||||
/* reduce size of the existing symbol */
|
||||
node->end = sym->end;
|
||||
}
|
||||
free(sym);
|
||||
return;
|
||||
}
|
||||
|
@ -397,6 +401,7 @@ static int zend_jit_disasm_init(void)
|
|||
REGISTER_HELPER(zend_jit_int_extend_stack_helper);
|
||||
REGISTER_HELPER(zend_jit_leave_nested_func_helper);
|
||||
REGISTER_HELPER(zend_jit_leave_top_func_helper);
|
||||
REGISTER_HELPER(zend_jit_leave_func_helper);
|
||||
REGISTER_HELPER(zend_jit_symtable_find);
|
||||
REGISTER_HELPER(zend_jit_hash_index_lookup_rw);
|
||||
REGISTER_HELPER(zend_jit_hash_index_lookup_w);
|
||||
|
@ -448,6 +453,7 @@ static int zend_jit_disasm_init(void)
|
|||
REGISTER_HELPER(zend_jit_pre_dec);
|
||||
REGISTER_HELPER(zend_runtime_jit);
|
||||
REGISTER_HELPER(zend_jit_hot_func);
|
||||
REGISTER_HELPER(zend_jit_check_constant);
|
||||
#undef REGISTER_HELPER
|
||||
|
||||
#ifndef _WIN32
|
||||
|
|
|
@ -33,18 +33,11 @@ extern int zend_jit_profile_counter_rid;
|
|||
|
||||
extern int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT];
|
||||
|
||||
void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend_op *opline);
|
||||
|
||||
typedef struct _zend_jit_op_array_extension {
|
||||
int16_t *counter;
|
||||
const void *orig_handlers[1];
|
||||
} zend_jit_op_array_extension;
|
||||
|
||||
static zend_always_inline zend_long zend_jit_op_array_hash(const zend_op_array *op_array)
|
||||
static zend_always_inline zend_long zend_jit_hash(const void *ptr)
|
||||
{
|
||||
uintptr_t x;
|
||||
|
||||
x = (uintptr_t)op_array->opcodes >> 3;
|
||||
x = (uintptr_t)ptr >> 3;
|
||||
#if SIZEOF_SIZE_T == 4
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
|
@ -57,6 +50,16 @@ static zend_always_inline zend_long zend_jit_op_array_hash(const zend_op_array *
|
|||
return x;
|
||||
}
|
||||
|
||||
void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend_op *opline);
|
||||
|
||||
typedef struct _zend_jit_op_array_hot_extension {
|
||||
int16_t *counter;
|
||||
const void *orig_handlers[1];
|
||||
} zend_jit_op_array_hot_extension;
|
||||
|
||||
#define zend_jit_op_array_hash(op_array) \
|
||||
zend_jit_hash((op_array)->opcodes)
|
||||
|
||||
extern const zend_op *zend_jit_halt_op;
|
||||
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
|
@ -78,6 +81,10 @@ extern const zend_op *zend_jit_halt_op;
|
|||
handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); \
|
||||
return; \
|
||||
} while(0)
|
||||
# define ZEND_OPCODE_TAIL_CALL_EX(handler, arg) do { \
|
||||
handler(arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); \
|
||||
return; \
|
||||
} while(0)
|
||||
#else
|
||||
# define EXECUTE_DATA_D zend_execute_data* execute_data
|
||||
# define EXECUTE_DATA_C execute_data
|
||||
|
@ -96,6 +103,9 @@ extern const zend_op *zend_jit_halt_op;
|
|||
# define ZEND_OPCODE_TAIL_CALL(handler) do { \
|
||||
return handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); \
|
||||
} while(0)
|
||||
# define ZEND_OPCODE_TAIL_CALL_EX(handler, arg) do { \
|
||||
return handler(arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
/* VM handlers */
|
||||
|
@ -104,6 +114,7 @@ typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *zend_vm_opcode_handler_t)(ZEND_O
|
|||
/* VM helpers */
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(uint32_t call_info EXECUTE_DATA_DC);
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_top_func_helper(uint32_t call_info EXECUTE_DATA_DC);
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_func_helper(uint32_t call_info EXECUTE_DATA_DC);
|
||||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_profile_helper(ZEND_OPCODE_HANDLER_ARGS);
|
||||
|
||||
|
@ -116,4 +127,267 @@ void ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D);
|
|||
void ZEND_FASTCALL zend_jit_get_constant(const zval *key, uint32_t flags);
|
||||
int ZEND_FASTCALL zend_jit_check_constant(const zval *key);
|
||||
|
||||
/* Tracer */
|
||||
#define zend_jit_opline_hash(opline) \
|
||||
zend_jit_hash(opline)
|
||||
|
||||
#define ZEND_JIT_TRACE_FUNC_COST (1*250)
|
||||
#define ZEND_JIT_TRACE_RET_COST (1*250-1)
|
||||
#define ZEND_JIT_TRACE_LOOP_COST (2*250)
|
||||
#define ZEND_JIT_TRACE_COUNTER_INIT (127*250)
|
||||
|
||||
#define ZEND_JIT_TRACE_MAX_TRACES 1024 /* max number of traces */
|
||||
#define ZEND_JIT_TRACE_MAX_LENGTH 1024 /* max length of single trace */
|
||||
#define ZEND_JIT_TRACE_MAX_EXITS 512 /* max number of side exits per trace */
|
||||
#define ZEND_JIT_TRACE_MAX_SIDE_TRACES 128 /* max number of side traces of a root trace */
|
||||
#define ZEND_JIT_TRACE_MAX_EXIT_COUNTERS 8192 /* max number of side exits for all trace */
|
||||
|
||||
#define ZEND_JIT_TRACE_MAX_FUNCS 30 /* max number of different functions in a single trace */
|
||||
#define ZEND_JIT_TRACE_MAX_CALL_DEPTH 10 /* max depth of inlined calls */
|
||||
#define ZEND_JIT_TRACE_MAX_RET_DEPTH 4 /* max depth of inlined returns */
|
||||
#define ZEND_JIT_TRACE_MAX_RECURSION 2 /* max number of recursive inlined calls */
|
||||
#define ZEND_JIT_TRACE_MAX_UNROLL_LOOPS 8 /* max number of unrolled loops */
|
||||
|
||||
#define ZEND_JIT_TRACE_HOT_SIDE_COUNT 8 /* number of exits before taking side trace */
|
||||
#define ZEND_JIT_TRACE_HOT_RETURN_COUNT 8 /* number of returns before taking continuation trace */
|
||||
|
||||
#define ZEND_JIT_TRACE_MAX_ROOT_FAILURES 16 /* number of attemts to record/compile a root trace */
|
||||
#define ZEND_JIT_TRACE_MAX_SIDE_FAILURES 4 /* number of attemts to record/compile a side trace */
|
||||
|
||||
#define ZEND_JIT_TRACE_BAD_ROOT_SLOTS 64 /* number of slots in bad root trace cache */
|
||||
|
||||
#define ZEND_JIT_TRACE_STOP(_) \
|
||||
_(LOOP, "loop") \
|
||||
_(RECURSIVE_CALL, "recursive call") \
|
||||
_(RECURSIVE_RET, "recursive return") \
|
||||
_(RETURN, "return") \
|
||||
_(LINK, "link to another trace") \
|
||||
/* compilation and linking successful */ \
|
||||
_(COMPILED, "compiled") \
|
||||
_(ALREADY_DONE, "already prcessed") \
|
||||
/* failures */ \
|
||||
_(ERROR, "error") /* not used */ \
|
||||
_(NOT_SUPPORTED, "not supported instructions") \
|
||||
_(EXCEPTION, "exception") \
|
||||
_(TOO_LONG, "trace too long") \
|
||||
_(TOO_DEEP, "trace too deep") \
|
||||
_(TOO_DEEP_RET, "trace too deep return") \
|
||||
_(DEEP_RECURSION, "deep recursion") \
|
||||
_(LOOP_UNROLL, "loop unroll limit reached") \
|
||||
_(LOOP_EXIT, "exit from loop") \
|
||||
_(BLACK_LIST, "trace blacklisted") \
|
||||
_(INNER_LOOP, "inner loop") /* trace it */ \
|
||||
_(COMPILED_LOOP, "compiled loop") \
|
||||
_(TOPLEVEL, "toplevel") \
|
||||
_(TRAMPOLINE, "trampoline call") \
|
||||
_(BAD_FUNC, "bad function call") \
|
||||
_(HALT, "exit from interpreter") \
|
||||
_(COMPILER_ERROR, "JIT compilation error") \
|
||||
/* no recoverable error (blacklist immediately) */ \
|
||||
_(NO_SHM, "insufficient shared memory") \
|
||||
_(TOO_MANY_TRACES, "too many traces") \
|
||||
_(TOO_MANY_CHILDREN, "too many side traces") \
|
||||
_(TOO_MANY_EXITS, "too many side exits") \
|
||||
|
||||
#define ZEND_JIT_TRACE_STOP_NAME(name, description) \
|
||||
ZEND_JIT_TRACE_STOP_ ## name,
|
||||
|
||||
typedef enum _zend_jit_trace_stop {
|
||||
ZEND_JIT_TRACE_STOP(ZEND_JIT_TRACE_STOP_NAME)
|
||||
} zend_jit_trace_stop;
|
||||
|
||||
#define ZEND_JIT_TRACE_STOP_OK(ret) \
|
||||
(ret < ZEND_JIT_TRACE_STOP_COMPILED)
|
||||
|
||||
#define ZEND_JIT_TRACE_STOP_DONE(ret) \
|
||||
(ret < ZEND_JIT_TRACE_STOP_ERROR)
|
||||
|
||||
#define ZEND_JIT_TRACE_STOP_REPEAT(ret) \
|
||||
(ret == ZEND_JIT_TRACE_STOP_INNER_LOOP)
|
||||
|
||||
#define ZEND_JIT_TRACE_STOP_MAY_RECOVER(ret) \
|
||||
(ret <= ZEND_JIT_TRACE_STOP_COMPILER_ERROR)
|
||||
|
||||
#define ZEND_JIT_TRACE_START_MASK 0xf
|
||||
|
||||
#define ZEND_JIT_TRACE_START_LOOP (1<<0)
|
||||
#define ZEND_JIT_TRACE_START_ENTER (1<<1)
|
||||
#define ZEND_JIT_TRACE_START_RETURN (1<<2)
|
||||
#define ZEND_JIT_TRACE_START_SIDE (1<<3) /* used for side traces */
|
||||
|
||||
#define ZEND_JIT_TRACE_JITED (1<<4)
|
||||
#define ZEND_JIT_TRACE_BLACKLISTED (1<<5)
|
||||
#define ZEND_JIT_TRACE_UNSUPPORTED (1<<6)
|
||||
|
||||
#define ZEND_JIT_TRACE_SUPPORTED 0
|
||||
|
||||
#define ZEND_JIT_EXIT_JITED (1<<0)
|
||||
#define ZEND_JIT_EXIT_BLACKLISTED (1<<1)
|
||||
|
||||
typedef union _zend_op_trace_info {
|
||||
zend_op dummy; /* the size of this structure must be the same as zend_op */
|
||||
struct {
|
||||
const void *orig_handler;
|
||||
const void *call_handler;
|
||||
int16_t *counter;
|
||||
uint8_t trace_flags;
|
||||
};
|
||||
} zend_op_trace_info;
|
||||
|
||||
typedef struct _zend_jit_op_array_trace_extension {
|
||||
zend_func_info func_info;
|
||||
size_t offset; /* offset from "zend_op" to corresponding "op_info" */
|
||||
zend_op_trace_info trace_info[1];
|
||||
} zend_jit_op_array_trace_extension;
|
||||
|
||||
#define ZEND_OP_TRACE_INFO(opline, offset) \
|
||||
((zend_op_trace_info*)(((char*)opline) + offset))
|
||||
|
||||
/* Recorder */
|
||||
typedef enum _zend_jit_trace_op {
|
||||
ZEND_JIT_TRACE_VM,
|
||||
ZEND_JIT_TRACE_OP1_TYPE,
|
||||
ZEND_JIT_TRACE_OP2_TYPE,
|
||||
ZEND_JIT_TRACE_INIT_CALL,
|
||||
ZEND_JIT_TRACE_DO_ICALL,
|
||||
ZEND_JIT_TRACE_ENTER,
|
||||
ZEND_JIT_TRACE_BACK,
|
||||
ZEND_JIT_TRACE_START,
|
||||
ZEND_JIT_TRACE_END,
|
||||
} zend_jit_trace_op;
|
||||
|
||||
#define IS_UNKNOWN 255 /* may be used for zend_jit_trace_rec.op?_type */
|
||||
#define IS_TRACE_REFERENCE (1<<5)
|
||||
|
||||
typedef struct _zend_jit_trace_rec {
|
||||
uint8_t op; /* zend_jit_trace_op */
|
||||
union {
|
||||
struct {
|
||||
uint8_t op1_type;/* recorded zval op1_type for ZEND_JIT_TRACE_VM */
|
||||
uint8_t op2_type;/* recorded zval op2_type for ZEND_JIT_TRACE_VM */
|
||||
uint8_t op3_type;/* recorded zval for op_data.op1_type for ZEND_JIT_TRACE_VM */
|
||||
};
|
||||
struct {
|
||||
union {
|
||||
uint8_t recursive; /* part of recursive return sequence for ZEND_JIT_TRACE_BACK */
|
||||
int8_t return_value_used; /* for ZEND_JIT_TRACE_ENTER */
|
||||
uint8_t fake; /* for ZEND_JIT_TRACE_INIT_FCALL */
|
||||
};
|
||||
uint8_t first_ssa_var; /* may be used for ZEND_JIT_TRACE_ENTER and ZEND_JIT_TRACE_BACK */
|
||||
};
|
||||
struct {
|
||||
uint8_t start; /* ZEND_JIT_TRACE_START_MASK for ZEND_JIT_TRACE_START/END */
|
||||
uint8_t stop; /* zend_jit_trace_stop for ZEND_JIT_TRACE_START/END */
|
||||
uint8_t level; /* recursive return level for ZEND_JIT_TRACE_START */
|
||||
};
|
||||
};
|
||||
union {
|
||||
const void *ptr;
|
||||
const zend_function *func;
|
||||
const zend_op_array *op_array;
|
||||
const zend_op *opline;
|
||||
const zend_class_entry *ce;
|
||||
};
|
||||
} zend_jit_trace_rec;
|
||||
|
||||
typedef struct _zend_jit_trace_start_rec {
|
||||
uint8_t op; /* zend_jit_trace_op */
|
||||
uint8_t start; /* ZEND_JIT_TRACE_START_MASK for ZEND_JIT_TRACE_START/END */
|
||||
uint8_t stop; /* zend_jit_trace_stop for ZEND_JIT_TRACE_START/END */
|
||||
uint8_t level; /* recursive return level for ZEND_JIT_TRACE_START */
|
||||
const zend_op_array *op_array;
|
||||
const zend_op *opline;
|
||||
} zend_jit_trace_start_rec;
|
||||
|
||||
#define ZEND_JIT_TRACE_START_REC_SIZE 2
|
||||
|
||||
typedef struct _zend_jit_trace_exit_info {
|
||||
const zend_op *opline; /* opline where VM should continue execution */
|
||||
uint32_t stack_size;
|
||||
uint32_t stack_offset;
|
||||
} zend_jit_trace_exit_info;
|
||||
|
||||
typedef int32_t zend_jit_trace_stack;
|
||||
|
||||
typedef struct _zend_jit_trace_info {
|
||||
uint32_t id; /* trace id */
|
||||
uint32_t root; /* root trace id or self id for root traces */
|
||||
uint32_t parent; /* parent trace id or 0 for root traces */
|
||||
uint32_t link; /* link trace id or self id for loop) */
|
||||
uint32_t exit_count; /* number of side exits */
|
||||
uint32_t child_count; /* number of side traces for root traces */
|
||||
uint32_t code_size; /* size of native code */
|
||||
uint32_t exit_counters; /* offset in exit counters array */
|
||||
uint32_t stack_map_size;
|
||||
const void *code_start; /* address of native code */
|
||||
zend_jit_trace_exit_info *exit_info; /* info about side exits */
|
||||
zend_jit_trace_stack *stack_map;
|
||||
//uint32_t loop_offset;
|
||||
} zend_jit_trace_info;
|
||||
|
||||
typedef struct _zend_jit_trace_stack_frame zend_jit_trace_stack_frame;
|
||||
|
||||
struct _zend_jit_trace_stack_frame {
|
||||
zend_jit_trace_stack_frame *call;
|
||||
zend_jit_trace_stack_frame *prev;
|
||||
const zend_function *func;
|
||||
union {
|
||||
struct {
|
||||
int8_t return_value_used;
|
||||
int8_t nested;
|
||||
int8_t num_args;
|
||||
};
|
||||
int return_ssa_var;
|
||||
};
|
||||
zend_jit_trace_stack stack[1];
|
||||
};
|
||||
|
||||
typedef struct _zend_jit_globals {
|
||||
zend_jit_trace_stack_frame *current_frame;
|
||||
|
||||
const zend_op *bad_root_cache_opline[ZEND_JIT_TRACE_BAD_ROOT_SLOTS];
|
||||
uint8_t bad_root_cache_count[ZEND_JIT_TRACE_BAD_ROOT_SLOTS];
|
||||
uint8_t bad_root_cache_stop[ZEND_JIT_TRACE_BAD_ROOT_SLOTS];
|
||||
uint32_t bad_root_slot;
|
||||
|
||||
uint8_t exit_counters[ZEND_JIT_TRACE_MAX_EXIT_COUNTERS];
|
||||
} zend_jit_globals;
|
||||
|
||||
#ifdef ZTS
|
||||
# define JIT_G(v) ZEND_TSRMG(zend_jit_globals_id, zend_jit_globals *, v)
|
||||
extern int zend_jit_globals_id;
|
||||
#else
|
||||
# define JIT_G(v) (jit_globals.v)
|
||||
extern zend_jit_globals jit_globals;
|
||||
#endif
|
||||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_trace_helper(ZEND_OPCODE_HANDLER_ARGS);
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_ret_trace_helper(ZEND_OPCODE_HANDLER_ARGS);
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_trace_helper(ZEND_OPCODE_HANDLER_ARGS);
|
||||
|
||||
int ZEND_FASTCALL zend_jit_trace_hot_root(zend_execute_data *execute_data, const zend_op *opline);
|
||||
int ZEND_FASTCALL zend_jit_trace_exit(uint32_t trace_num, uint32_t exit_num);
|
||||
zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *execute_data, const zend_op *opline, zend_jit_trace_rec *trace_buffer, uint8_t start);
|
||||
|
||||
static zend_always_inline const zend_op* zend_jit_trace_get_exit_opline(zend_jit_trace_rec *trace, const zend_op *opline, zend_bool *exit_if_true)
|
||||
{
|
||||
if (trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END) {
|
||||
if (trace->opline == opline + 1) {
|
||||
/* not taken branch */
|
||||
*exit_if_true = opline->opcode == ZEND_JMPNZ;
|
||||
return OP_JMP_ADDR(opline, opline->op2);
|
||||
} else if (trace->opline == OP_JMP_ADDR(opline, opline->op2)) {
|
||||
/* taken branch */
|
||||
*exit_if_true = opline->opcode == ZEND_JMPZ;
|
||||
return opline + 1;
|
||||
} else {
|
||||
ZEND_ASSERT(0);
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(0);
|
||||
}
|
||||
*exit_if_true = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* ZEND_JIT_INTERNAL_H */
|
||||
|
|
3972
ext/opcache/jit/zend_jit_trace.c
Normal file
3972
ext/opcache/jit/zend_jit_trace.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <ZendAccelerator.h>
|
||||
#include "Optimizer/zend_func_info.h"
|
||||
#include "Optimizer/zend_call_graph.h"
|
||||
#include "zend_jit.h"
|
||||
#include "zend_jit_internal.h"
|
||||
|
||||
|
@ -98,6 +99,15 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_top_func_helper(uint32_t ca
|
|||
#endif
|
||||
}
|
||||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_func_helper(uint32_t call_info EXECUTE_DATA_DC)
|
||||
{
|
||||
if (call_info & ZEND_CALL_TOP) {
|
||||
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_leave_top_func_helper, call_info);
|
||||
} else {
|
||||
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_leave_nested_func_helper, call_info);
|
||||
}
|
||||
}
|
||||
|
||||
void ZEND_FASTCALL zend_jit_copy_extra_args_helper(EXECUTE_DATA_D)
|
||||
{
|
||||
zend_op_array *op_array = &EX(func)->op_array;
|
||||
|
@ -182,8 +192,8 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_profile_helper(ZEND_OPCODE_HANDLE
|
|||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_counter_helper(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
zend_jit_op_array_extension *jit_extension =
|
||||
(zend_jit_op_array_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
|
||||
zend_jit_op_array_hot_extension *jit_extension =
|
||||
(zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
|
||||
#ifndef HAVE_GCC_GLOBAL_REGS
|
||||
const zend_op *opline = EX(opline);
|
||||
#endif
|
||||
|
@ -191,6 +201,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_counter_helper(ZEND_OPCODE_H
|
|||
*(jit_extension->counter) -= ZEND_JIT_HOT_FUNC_COST;
|
||||
|
||||
if (UNEXPECTED(*(jit_extension->counter) <= 0)) {
|
||||
*(jit_extension->counter) = ZEND_JIT_HOT_COUNTER_INIT;
|
||||
zend_jit_hot_func(execute_data, opline);
|
||||
ZEND_OPCODE_RETURN();
|
||||
} else {
|
||||
|
@ -201,8 +212,8 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_counter_helper(ZEND_OPCODE_H
|
|||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_counter_helper(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
zend_jit_op_array_extension *jit_extension =
|
||||
(zend_jit_op_array_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
|
||||
zend_jit_op_array_hot_extension *jit_extension =
|
||||
(zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
|
||||
#ifndef HAVE_GCC_GLOBAL_REGS
|
||||
const zend_op *opline = EX(opline);
|
||||
#endif
|
||||
|
@ -210,6 +221,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_counter_helper(ZEND_OPCODE_H
|
|||
*(jit_extension->counter) -= ZEND_JIT_HOT_LOOP_COST;
|
||||
|
||||
if (UNEXPECTED(*(jit_extension->counter) <= 0)) {
|
||||
*(jit_extension->counter) = ZEND_JIT_HOT_COUNTER_INIT;
|
||||
zend_jit_hot_func(execute_data, opline);
|
||||
ZEND_OPCODE_RETURN();
|
||||
} else {
|
||||
|
@ -266,3 +278,607 @@ int ZEND_FASTCALL zend_jit_check_constant(const zval *key)
|
|||
{
|
||||
return _zend_quick_get_constant(key, 0, 1);
|
||||
}
|
||||
|
||||
static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_trace_counter_helper(uint32_t cost ZEND_OPCODE_HANDLER_ARGS_DC)
|
||||
{
|
||||
zend_jit_op_array_trace_extension *jit_extension =
|
||||
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
|
||||
size_t offset = jit_extension->offset;
|
||||
#ifndef HAVE_GCC_GLOBAL_REGS
|
||||
const zend_op *opline = EX(opline);
|
||||
#endif
|
||||
|
||||
*(ZEND_OP_TRACE_INFO(opline, offset)->counter) -= ZEND_JIT_TRACE_FUNC_COST;
|
||||
|
||||
if (UNEXPECTED(*(ZEND_OP_TRACE_INFO(opline, offset)->counter) <= 0)) {
|
||||
*(ZEND_OP_TRACE_INFO(opline, offset)->counter) = ZEND_JIT_TRACE_COUNTER_INIT;
|
||||
if (UNEXPECTED(zend_jit_trace_hot_root(execute_data, opline) < 0)) {
|
||||
#ifndef HAVE_GCC_GLOBAL_REGS
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
execute_data = EG(current_execute_data);
|
||||
opline = EX(opline);
|
||||
return;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
} else {
|
||||
zend_vm_opcode_handler_t handler = (zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->orig_handler;
|
||||
ZEND_OPCODE_TAIL_CALL(handler);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_trace_helper(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper, ZEND_JIT_TRACE_FUNC_COST);
|
||||
}
|
||||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_ret_trace_helper(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper, ZEND_JIT_TRACE_RET_COST);
|
||||
}
|
||||
|
||||
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_trace_helper(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
ZEND_OPCODE_TAIL_CALL_EX(zend_jit_trace_counter_helper, ZEND_JIT_TRACE_LOOP_COST);
|
||||
}
|
||||
|
||||
#define TRACE_RECORD(_op, _ptr) \
|
||||
trace_buffer[idx].op = _op; \
|
||||
trace_buffer[idx].ptr = _ptr; \
|
||||
idx++; \
|
||||
if (idx >= ZEND_JIT_TRACE_MAX_LENGTH - 1) { \
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_LONG; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define TRACE_RECORD_VM(_op, _ptr, _op1_type, _op2_type, _op3_type) \
|
||||
trace_buffer[idx].op = _op; \
|
||||
trace_buffer[idx].op1_type = _op1_type; \
|
||||
trace_buffer[idx].op2_type = _op2_type; \
|
||||
trace_buffer[idx].op3_type = _op3_type; \
|
||||
trace_buffer[idx].ptr = _ptr; \
|
||||
idx++; \
|
||||
if (idx >= ZEND_JIT_TRACE_MAX_LENGTH - 1) { \
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_LONG; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define TRACE_RECORD_ENTER(_op, _return_value_used, _ptr) \
|
||||
trace_buffer[idx].op = _op; \
|
||||
trace_buffer[idx].return_value_used = _return_value_used; \
|
||||
trace_buffer[idx].ptr = _ptr; \
|
||||
idx++; \
|
||||
if (idx >= ZEND_JIT_TRACE_MAX_LENGTH - 1) { \
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_LONG; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define TRACE_RECORD_INIT(_op, _fake, _ptr) \
|
||||
trace_buffer[idx].op = _op; \
|
||||
trace_buffer[idx].fake = _fake; \
|
||||
trace_buffer[idx].ptr = _ptr; \
|
||||
idx++; \
|
||||
if (idx >= ZEND_JIT_TRACE_MAX_LENGTH - 1) { \
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_LONG; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define TRACE_RECORD_BACK(_op, _recursive, _ptr) \
|
||||
trace_buffer[idx].op = _op; \
|
||||
trace_buffer[idx].recursive = _recursive; \
|
||||
trace_buffer[idx].ptr = _ptr; \
|
||||
idx++; \
|
||||
if (idx >= ZEND_JIT_TRACE_MAX_LENGTH - 1) { \
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_LONG; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define TRACE_RECORD_TYPE(_op, _ptr) \
|
||||
trace_buffer[idx].op = _op; \
|
||||
trace_buffer[idx].ptr = _ptr; \
|
||||
idx++; \
|
||||
if (idx >= ZEND_JIT_TRACE_MAX_LENGTH - 1) { \
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_LONG; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define TRACE_START(_op, _start, _ptr) \
|
||||
trace_buffer[0].op = _op; \
|
||||
trace_buffer[0].start = _start; \
|
||||
trace_buffer[0].level = 0; \
|
||||
trace_buffer[0].ptr = _ptr; \
|
||||
idx = ZEND_JIT_TRACE_START_REC_SIZE;
|
||||
|
||||
#define TRACE_END(_op, _stop, _ptr) \
|
||||
trace_buffer[idx].op = _op; \
|
||||
trace_buffer[idx].start = trace_buffer[idx].start; \
|
||||
trace_buffer[idx].stop = trace_buffer[0].stop = _stop; \
|
||||
trace_buffer[idx].level = trace_buffer[0].level = ret_level ? ret_level + 1 : 0; \
|
||||
trace_buffer[idx].ptr = _ptr;
|
||||
|
||||
#ifndef ZEND_JIT_RECORD_RECURSIVE_RETURN
|
||||
# define ZEND_JIT_RECORD_RECURSIVE_RETURN 1
|
||||
#endif
|
||||
|
||||
static int zend_jit_trace_recursive_call_count(const zend_op_array *op_array, const zend_op_array **unrolled_calls, int ret_level, int level)
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
|
||||
for (i = ret_level; i < level; i++) {
|
||||
count += (unrolled_calls[i] == op_array);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_recursive_ret_count(const zend_op_array *op_array, const zend_op_array **unrolled_calls, int ret_level)
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
|
||||
for (i = 0; i < ret_level; i++) {
|
||||
count += (unrolled_calls[i] == op_array);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_has_recursive_ret(zend_execute_data *ex, const zend_op_array *orig_op_array, const zend_op *orig_opline, int ret_level)
|
||||
{
|
||||
while (ex != NULL && ret_level < ZEND_JIT_TRACE_MAX_RET_DEPTH) {
|
||||
if (&ex->func->op_array == orig_op_array && ex->opline + 1 == orig_opline) {
|
||||
return 1;
|
||||
}
|
||||
ex = ex->prev_execute_data;
|
||||
ret_level++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_bad_inner_loop(const zend_op *opline)
|
||||
{
|
||||
const zend_op **cache_opline = JIT_G(bad_root_cache_opline);
|
||||
uint8_t *cache_count = JIT_G(bad_root_cache_count);
|
||||
uint8_t *cache_stop = JIT_G(bad_root_cache_stop);
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < ZEND_JIT_TRACE_BAD_ROOT_SLOTS; i++) {
|
||||
if (cache_opline[i] == opline) {
|
||||
if ((cache_stop[i] == ZEND_JIT_TRACE_STOP_INNER_LOOP
|
||||
|| cache_stop[i] == ZEND_JIT_TRACE_STOP_LOOP_EXIT)
|
||||
&& cache_count[i] > ZEND_JIT_TRACE_MAX_ROOT_FAILURES / 2) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_bad_compiled_loop(const zend_op *opline)
|
||||
{
|
||||
const zend_op **cache_opline = JIT_G(bad_root_cache_opline);
|
||||
uint8_t *cache_count = JIT_G(bad_root_cache_count);
|
||||
uint8_t *cache_stop = JIT_G(bad_root_cache_stop);
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < ZEND_JIT_TRACE_BAD_ROOT_SLOTS; i++) {
|
||||
if (cache_opline[i] == opline) {
|
||||
if (cache_stop[i] == ZEND_JIT_TRACE_STOP_COMPILED_LOOP
|
||||
&& cache_count[i] >= ZEND_JIT_TRACE_MAX_ROOT_FAILURES - 1) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_bad_loop_exit(const zend_op *opline)
|
||||
{
|
||||
const zend_op **cache_opline = JIT_G(bad_root_cache_opline);
|
||||
uint8_t *cache_count = JIT_G(bad_root_cache_count);
|
||||
uint8_t *cache_stop = JIT_G(bad_root_cache_stop);
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < ZEND_JIT_TRACE_BAD_ROOT_SLOTS; i++) {
|
||||
if (cache_opline[i] == opline) {
|
||||
if (cache_stop[i] == ZEND_JIT_TRACE_STOP_LOOP_EXIT
|
||||
&& cache_count[i] >= ZEND_JIT_TRACE_MAX_ROOT_FAILURES - 1) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_record_fake_init_call(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx)
|
||||
{
|
||||
zend_jit_trace_stop stop ZEND_ATTRIBUTE_UNUSED = ZEND_JIT_TRACE_STOP_ERROR;
|
||||
|
||||
do {
|
||||
if (call->prev_execute_data) {
|
||||
idx = zend_jit_trace_record_fake_init_call(call->prev_execute_data, trace_buffer, idx);
|
||||
}
|
||||
TRACE_RECORD_INIT(ZEND_JIT_TRACE_INIT_CALL, 1, call->func);
|
||||
} while (0);
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trace Linking Rules
|
||||
* ===================
|
||||
*
|
||||
* flags
|
||||
* +----------+----------+----------++----------+----------+----------+
|
||||
* | || JIT |
|
||||
* +----------+----------+----------++----------+----------+----------+
|
||||
* start | LOOP | ENTER | RETURN || LOOP | ENTER | RETURN |
|
||||
* +========+==========+==========+==========++==========+==========+==========+
|
||||
* | LOOP | loop | | loop-ret || COMPILED | LINK | LINK |
|
||||
* +--------+----------+----------+----------++----------+----------+----------+
|
||||
* | ENTER |INNER_LOOP| rec-call | return || LINK | LINK | LINK |
|
||||
* +--------+----------+----------+----------++----------+----------+----------+
|
||||
* | RETURN |INNER_LOOP| | rec-ret || LINK | | LINK |
|
||||
* +--------+----------+----------+----------++----------+----------+----------+
|
||||
* | SIDE | unroll | | return || LINK | LINK | LINK |
|
||||
* +--------+----------+----------+----------++----------+----------+----------+
|
||||
*
|
||||
* loop: LOOP if "cycle" and level == 0, otherwise INNER_LOOP
|
||||
* INNER_LOOP: abort recording and start new one (wit for loop)
|
||||
* COMPILED: abort recording (wait while side exit creates outer loop)
|
||||
* unroll: continue recording while unroll limit reached
|
||||
* rec-call: RECURSIVE_CALL if "cycle" and level > N, otherwise continue
|
||||
* loop-ret: LOOP_EXIT if level == 0, otherwise continue (wait for loop)
|
||||
* return: RETURN if level == 0
|
||||
* rec_ret: RECURSIVE_RET if "cycle" and ret_level > N, otherwise continue
|
||||
*
|
||||
*/
|
||||
|
||||
zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, const zend_op *op, zend_jit_trace_rec *trace_buffer, uint8_t start)
|
||||
{
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
zend_execute_data *save_execute_data = execute_data;
|
||||
const zend_op *save_opline = opline;
|
||||
#endif
|
||||
const zend_op *orig_opline;
|
||||
zend_jit_trace_stop stop = ZEND_JIT_TRACE_STOP_ERROR;
|
||||
int level = 0;
|
||||
int ret_level = 0;
|
||||
zend_vm_opcode_handler_t handler;
|
||||
zend_jit_op_array_trace_extension *jit_extension;
|
||||
size_t offset;
|
||||
int idx, count;
|
||||
uint8_t trace_flags, op1_type, op2_type, op3_type;
|
||||
int backtrack_recursion = -1;
|
||||
int backtrack_ret_recursion = -1;
|
||||
int backtrack_ret_recursion_level = 0;
|
||||
int loop_unroll_limit = 0;
|
||||
const zend_op_array *unrolled_calls[ZEND_JIT_TRACE_MAX_CALL_DEPTH + ZEND_JIT_TRACE_MAX_RET_DEPTH];
|
||||
#if ZEND_JIT_DETECT_UNROLLED_LOOPS
|
||||
uint32_t unrolled_loops[ZEND_JIT_TRACE_MAX_UNROLL_LOOPS];
|
||||
#endif
|
||||
zend_bool is_toplevel;
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
zend_execute_data *prev_execute_data = ex;
|
||||
|
||||
execute_data = ex;
|
||||
opline = EX(opline) = op;
|
||||
#else
|
||||
int rc;
|
||||
zend_execute_data *execute_data = ex;
|
||||
const zend_op *opline = EX(opline);
|
||||
#endif
|
||||
zend_execute_data *prev_call = EX(call);
|
||||
|
||||
orig_opline = opline;
|
||||
|
||||
jit_extension =
|
||||
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
|
||||
offset = jit_extension->offset;
|
||||
|
||||
TRACE_START(ZEND_JIT_TRACE_START, start, &EX(func)->op_array);
|
||||
((zend_jit_trace_start_rec*)trace_buffer)->opline = opline;
|
||||
is_toplevel = EX(func)->op_array.function_name == NULL;
|
||||
|
||||
|
||||
if (prev_call) {
|
||||
idx = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (UNEXPECTED(opline->opcode == ZEND_HANDLE_EXCEPTION)) {
|
||||
/* Abort trace because of exception */
|
||||
stop = ZEND_JIT_TRACE_STOP_EXCEPTION;
|
||||
break;
|
||||
}
|
||||
|
||||
op1_type = op2_type = op3_type = IS_UNKNOWN;
|
||||
if ((opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV))
|
||||
&& (opline->opcode != ZEND_ROPE_ADD && opline->opcode != ZEND_ROPE_END)) {
|
||||
zval *zv = EX_VAR(opline->op1.var);
|
||||
op1_type = Z_TYPE_P(zv);
|
||||
if (op1_type == IS_INDIRECT) {
|
||||
zv = Z_INDIRECT_P(zv);
|
||||
op1_type = Z_TYPE_P(zv);
|
||||
}
|
||||
if (op1_type == IS_REFERENCE) {
|
||||
op1_type = Z_TYPE_P(Z_REFVAL_P(zv)) | IS_TRACE_REFERENCE;
|
||||
}
|
||||
}
|
||||
if (opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
zval *zv = EX_VAR(opline->op2.var);
|
||||
op2_type = Z_TYPE_P(zv);
|
||||
if (op2_type == IS_INDIRECT) {
|
||||
zv = Z_INDIRECT_P(zv);
|
||||
op2_type = Z_TYPE_P(zv);
|
||||
}
|
||||
if (op2_type == IS_REFERENCE) {
|
||||
op2_type = Z_TYPE_P(Z_REFVAL_P(zv)) | IS_TRACE_REFERENCE;
|
||||
}
|
||||
}
|
||||
if (opline->opcode == ZEND_ASSIGN_DIM ||
|
||||
opline->opcode == ZEND_ASSIGN_OBJ ||
|
||||
opline->opcode == ZEND_ASSIGN_STATIC_PROP ||
|
||||
opline->opcode == ZEND_ASSIGN_DIM_OP ||
|
||||
opline->opcode == ZEND_ASSIGN_OBJ_OP ||
|
||||
opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP ||
|
||||
opline->opcode == ZEND_ASSIGN_OBJ_REF ||
|
||||
opline->opcode == ZEND_ASSIGN_STATIC_PROP_REF) {
|
||||
if ((opline+1)->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
zval *zv = EX_VAR((opline+1)->op1.var);
|
||||
op3_type = Z_TYPE_P(zv);
|
||||
if (op3_type == IS_INDIRECT) {
|
||||
zv = Z_INDIRECT_P(zv);
|
||||
op3_type = Z_TYPE_P(zv);
|
||||
}
|
||||
if (op3_type == IS_REFERENCE) {
|
||||
op3_type = Z_TYPE_P(Z_REFVAL_P(zv)) | IS_TRACE_REFERENCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_RECORD_VM(ZEND_JIT_TRACE_VM, opline, op1_type, op2_type, op3_type);
|
||||
|
||||
if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
zval *var = EX_VAR(opline->op1.var);
|
||||
uint8_t type = Z_TYPE_P(var);
|
||||
|
||||
if (type == IS_OBJECT) {
|
||||
TRACE_RECORD_TYPE(ZEND_JIT_TRACE_OP1_TYPE, Z_OBJCE_P(var));
|
||||
}
|
||||
}
|
||||
|
||||
if (opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
zval *var = EX_VAR(opline->op2.var);
|
||||
uint8_t type = Z_TYPE_P(var);
|
||||
|
||||
if (type == IS_OBJECT) {
|
||||
TRACE_RECORD_TYPE(ZEND_JIT_TRACE_OP2_TYPE, Z_OBJCE_P(var));
|
||||
}
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
if (EX(call)->func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
TRACE_RECORD(ZEND_JIT_TRACE_DO_ICALL, EX(call)->func);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
handler = (zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler;
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
handler();
|
||||
if (UNEXPECTED(opline == zend_jit_halt_op)) {
|
||||
stop = ZEND_JIT_TRACE_STOP_HALT;
|
||||
break;
|
||||
}
|
||||
if (UNEXPECTED(execute_data != prev_execute_data)) {
|
||||
if (execute_data->prev_execute_data == prev_execute_data) {
|
||||
#else
|
||||
rc = handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
|
||||
if (rc != 0) {
|
||||
if (rc < 0) {
|
||||
stop = ZEND_JIT_TRACE_STOP_HALT;
|
||||
break;
|
||||
}
|
||||
execute_data = EG(current_execute_data);
|
||||
opline = EX(opline);
|
||||
if (rc == 1) {
|
||||
#endif
|
||||
/* Enter into function */
|
||||
if (level > ZEND_JIT_TRACE_MAX_CALL_DEPTH) {
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_DEEP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (EX(func)->op_array.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
|
||||
/* TODO: Can we continue recording ??? */
|
||||
stop = ZEND_JIT_TRACE_STOP_TRAMPOLINE;
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_RECORD_ENTER(ZEND_JIT_TRACE_ENTER, EX(return_value) != NULL, &EX(func)->op_array);
|
||||
|
||||
count = zend_jit_trace_recursive_call_count(&EX(func)->op_array, unrolled_calls, ret_level, level);
|
||||
|
||||
if (opline == orig_opline) {
|
||||
if (count + 1 >= ZEND_JIT_TRACE_MAX_RECURSION) {
|
||||
stop = ZEND_JIT_TRACE_STOP_RECURSIVE_CALL;
|
||||
break;
|
||||
}
|
||||
backtrack_recursion = idx;
|
||||
} else if (count >= ZEND_JIT_TRACE_MAX_RECURSION) {
|
||||
stop = ZEND_JIT_TRACE_STOP_DEEP_RECURSION;
|
||||
break;
|
||||
}
|
||||
|
||||
unrolled_calls[ret_level + level] = &EX(func)->op_array;
|
||||
level++;
|
||||
} else {
|
||||
/* Return from function */
|
||||
if (level == 0) {
|
||||
if (is_toplevel) {
|
||||
stop = ZEND_JIT_TRACE_STOP_TOPLEVEL;
|
||||
break;
|
||||
#if ZEND_JIT_RECORD_RECURSIVE_RETURN
|
||||
} else if (start == ZEND_JIT_TRACE_START_RETURN
|
||||
&& execute_data->prev_execute_data
|
||||
&& execute_data->prev_execute_data->func
|
||||
&& execute_data->prev_execute_data->func->type == ZEND_USER_FUNCTION
|
||||
&& zend_jit_trace_has_recursive_ret(execute_data, trace_buffer[0].op_array, orig_opline, ret_level)) {
|
||||
if (ret_level > ZEND_JIT_TRACE_MAX_RET_DEPTH) {
|
||||
stop = ZEND_JIT_TRACE_STOP_TOO_DEEP_RET;
|
||||
break;
|
||||
}
|
||||
TRACE_RECORD_BACK(ZEND_JIT_TRACE_BACK, 1, &EX(func)->op_array);
|
||||
count = zend_jit_trace_recursive_ret_count(&EX(func)->op_array, unrolled_calls, ret_level);
|
||||
if (opline == orig_opline) {
|
||||
if (count + 1 >= ZEND_JIT_TRACE_MAX_RECURSION) {
|
||||
stop = ZEND_JIT_TRACE_STOP_RECURSIVE_RET;
|
||||
break;
|
||||
}
|
||||
backtrack_ret_recursion = idx;
|
||||
backtrack_ret_recursion_level = ret_level;
|
||||
} else if (count >= ZEND_JIT_TRACE_MAX_RECURSION) {
|
||||
stop = ZEND_JIT_TRACE_STOP_DEEP_RECURSION;
|
||||
break;
|
||||
}
|
||||
|
||||
unrolled_calls[ret_level] = &EX(func)->op_array;
|
||||
ret_level++;
|
||||
is_toplevel = EX(func)->op_array.function_name == NULL;
|
||||
#endif
|
||||
} else if (start & ZEND_JIT_TRACE_START_LOOP
|
||||
&& !zend_jit_trace_bad_loop_exit(orig_opline)) {
|
||||
/* Fail to try close the loop.
|
||||
If this doesn't work terminate it. */
|
||||
stop = ZEND_JIT_TRACE_STOP_LOOP_EXIT;
|
||||
break;
|
||||
} else {
|
||||
stop = ZEND_JIT_TRACE_STOP_RETURN;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
level--;
|
||||
TRACE_RECORD_BACK(ZEND_JIT_TRACE_BACK, 0, &EX(func)->op_array);
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
prev_execute_data = execute_data;
|
||||
#endif
|
||||
jit_extension =
|
||||
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&EX(func)->op_array);
|
||||
if (UNEXPECTED(!jit_extension)) {
|
||||
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
|
||||
break;
|
||||
}
|
||||
offset = jit_extension->offset;
|
||||
}
|
||||
if (EX(call) != prev_call) {
|
||||
if (trace_buffer[idx-1].op != ZEND_JIT_TRACE_BACK
|
||||
&& EX(call)
|
||||
&& EX(call)->prev_execute_data == prev_call) {
|
||||
if (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
|
||||
/* TODO: Can we continue recording ??? */
|
||||
stop = ZEND_JIT_TRACE_STOP_TRAMPOLINE;
|
||||
break;
|
||||
}
|
||||
TRACE_RECORD_INIT(ZEND_JIT_TRACE_INIT_CALL, 0, EX(call)->func);
|
||||
}
|
||||
prev_call = EX(call);
|
||||
}
|
||||
|
||||
#ifndef HAVE_GCC_GLOBAL_REGS
|
||||
opline = EX(opline);
|
||||
#endif
|
||||
|
||||
trace_flags = ZEND_OP_TRACE_INFO(opline, offset)->trace_flags;
|
||||
if (trace_flags) {
|
||||
if (trace_flags & ZEND_JIT_TRACE_JITED) {
|
||||
if (trace_flags & ZEND_JIT_TRACE_START_LOOP) {
|
||||
if ((start & ZEND_JIT_TRACE_START_LOOP) != 0
|
||||
&& level + ret_level == 0
|
||||
&& !zend_jit_trace_bad_compiled_loop(orig_opline)) {
|
||||
/* Fail to try close outer loop throgh side exit.
|
||||
If this doesn't work just link. */
|
||||
stop = ZEND_JIT_TRACE_STOP_COMPILED_LOOP;
|
||||
break;
|
||||
} else {
|
||||
stop = ZEND_JIT_TRACE_STOP_LINK;
|
||||
break;
|
||||
}
|
||||
} else if (trace_flags & ZEND_JIT_TRACE_START_ENTER) {
|
||||
if (start != ZEND_JIT_TRACE_START_RETURN) {
|
||||
// TODO: We may try to inline function ???
|
||||
stop = ZEND_JIT_TRACE_STOP_LINK;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
stop = ZEND_JIT_TRACE_STOP_LINK;
|
||||
break;
|
||||
}
|
||||
} else if (trace_flags & ZEND_JIT_TRACE_BLACKLISTED) {
|
||||
stop = ZEND_JIT_TRACE_STOP_BLACK_LIST;
|
||||
break;
|
||||
} else if (trace_flags & ZEND_JIT_TRACE_START_LOOP) {
|
||||
if (start != ZEND_JIT_TRACE_START_SIDE) {
|
||||
if (opline == orig_opline && level + ret_level == 0) {
|
||||
stop = ZEND_JIT_TRACE_STOP_LOOP;
|
||||
break;
|
||||
}
|
||||
/* Fail to try creating a trace for inner loop first.
|
||||
If this doesn't work try unroling loop. */
|
||||
if (!zend_jit_trace_bad_inner_loop(opline)) {
|
||||
stop = ZEND_JIT_TRACE_STOP_INNER_LOOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loop_unroll_limit < ZEND_JIT_TRACE_MAX_UNROLL_LOOPS) {
|
||||
loop_unroll_limit++;
|
||||
} else {
|
||||
stop = ZEND_JIT_TRACE_STOP_LOOP_UNROLL;
|
||||
break;
|
||||
}
|
||||
} else if (trace_flags & ZEND_JIT_TRACE_UNSUPPORTED) {
|
||||
TRACE_RECORD(ZEND_JIT_TRACE_VM, opline);
|
||||
stop = ZEND_JIT_TRACE_STOP_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ZEND_JIT_TRACE_STOP_OK(stop)) {
|
||||
if (backtrack_recursion > 0) {
|
||||
idx = backtrack_recursion;
|
||||
stop = ZEND_JIT_TRACE_STOP_RECURSIVE_CALL;
|
||||
} else if (backtrack_ret_recursion > 0) {
|
||||
idx = backtrack_ret_recursion;
|
||||
ret_level = backtrack_ret_recursion_level;
|
||||
stop = ZEND_JIT_TRACE_STOP_RECURSIVE_RET;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_END(ZEND_JIT_TRACE_END, stop, opline);
|
||||
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
if (stop != ZEND_JIT_TRACE_STOP_HALT) {
|
||||
EX(opline) = opline;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
execute_data = save_execute_data;
|
||||
opline = save_opline;
|
||||
#endif
|
||||
|
||||
return stop;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue