mirror of
https://github.com/php/php-src.git
synced 2025-08-16 22:18:50 +02:00
Improved SSCP integration
This commit is contained in:
parent
d17ed887f3
commit
f810c6f7c4
3 changed files with 139 additions and 120 deletions
|
@ -319,6 +319,126 @@ static zend_bool opline_supports_assign_contraction(
|
|||
return 1;
|
||||
}
|
||||
|
||||
int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
|
||||
{
|
||||
zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
|
||||
int removed_ops = 0;
|
||||
|
||||
if (func_info->callee_info) {
|
||||
zend_call_info *call_info = func_info->callee_info;
|
||||
static zend_function *in_array_function = NULL;
|
||||
|
||||
if (!in_array_function) {
|
||||
in_array_function = zend_hash_str_find_ptr(CG(function_table), "in_array", sizeof("in_array")-1);
|
||||
}
|
||||
do {
|
||||
if (call_info->callee_func == in_array_function
|
||||
&& (call_info->caller_init_opline->extended_value == 2
|
||||
|| (call_info->caller_init_opline->extended_value == 3
|
||||
&& (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
|
||||
&& (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
|
||||
|
||||
zend_op *send_array;
|
||||
zend_op *send_needly;
|
||||
zend_bool strict = 0;
|
||||
|
||||
if (call_info->caller_init_opline->extended_value == 2) {
|
||||
send_array = call_info->caller_call_opline - 1;
|
||||
send_needly = call_info->caller_call_opline - 2;
|
||||
} else {
|
||||
if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
|
||||
strict = 1;
|
||||
}
|
||||
send_array = call_info->caller_call_opline - 2;
|
||||
send_needly = call_info->caller_call_opline - 3;
|
||||
}
|
||||
|
||||
if (send_array->opcode == ZEND_SEND_VAL
|
||||
&& send_array->op1_type == IS_CONST
|
||||
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
|
||||
&& (send_needly->opcode == ZEND_SEND_VAL
|
||||
|| send_needly->opcode == ZEND_SEND_VAR)
|
||||
) {
|
||||
int ok = 1;
|
||||
|
||||
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
|
||||
HashTable *dst;
|
||||
zval *val, tmp;
|
||||
zend_ulong idx;
|
||||
|
||||
ZVAL_TRUE(&tmp);
|
||||
dst = emalloc(sizeof(HashTable));
|
||||
zend_hash_init(dst, zend_hash_num_elements(src), NULL, ZVAL_PTR_DTOR, 0);
|
||||
if (strict) {
|
||||
ZEND_HASH_FOREACH_VAL(src, val) {
|
||||
if (Z_TYPE_P(val) == IS_STRING) {
|
||||
zend_hash_add(dst, Z_STR_P(val), &tmp);
|
||||
} else if (Z_TYPE_P(val) == IS_LONG) {
|
||||
zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
|
||||
} else {
|
||||
zend_array_destroy(dst);
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
} else {
|
||||
ZEND_HASH_FOREACH_VAL(src, val) {
|
||||
if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
|
||||
zend_array_destroy(dst);
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
zend_hash_add(dst, Z_STR_P(val), &tmp);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
uint32_t op_num = send_needly - op_array->opcodes;
|
||||
zend_ssa_op *ssa_op = ssa->ops + op_num;
|
||||
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
/* Reconstruct SSA */
|
||||
int var_num = ssa_op->op1_use;
|
||||
zend_ssa_var *var = ssa->vars + var_num;
|
||||
|
||||
ZEND_ASSERT(ssa_op->op1_def < 0);
|
||||
zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
|
||||
ssa_op->op1_use = -1;
|
||||
ssa_op->op1_use_chain = -1;
|
||||
op_num = call_info->caller_call_opline - op_array->opcodes;
|
||||
ssa_op = ssa->ops + op_num;
|
||||
ssa_op->op1_use = var_num;
|
||||
ssa_op->op1_use_chain = var->use_chain;
|
||||
var->use_chain = op_num;
|
||||
}
|
||||
|
||||
ZVAL_ARR(&tmp, dst);
|
||||
|
||||
/* Update opcode */
|
||||
call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
|
||||
call_info->caller_call_opline->extended_value = strict;
|
||||
call_info->caller_call_opline->op1_type = send_needly->op1_type;
|
||||
call_info->caller_call_opline->op1.num = send_needly->op1.num;
|
||||
call_info->caller_call_opline->op2_type = IS_CONST;
|
||||
call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
|
||||
if (call_info->caller_init_opline->extended_value == 3) {
|
||||
MAKE_NOP(call_info->caller_call_opline - 1);
|
||||
}
|
||||
MAKE_NOP(call_info->caller_init_opline);
|
||||
MAKE_NOP(send_needly);
|
||||
MAKE_NOP(send_array);
|
||||
removed_ops++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
call_info = call_info->next_callee;
|
||||
} while (call_info);
|
||||
}
|
||||
|
||||
return removed_ops;
|
||||
}
|
||||
|
||||
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
|
||||
{
|
||||
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
|
||||
|
@ -332,8 +452,18 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
|
|||
zend_op *opline;
|
||||
zval tmp;
|
||||
|
||||
if (sccp_optimize_op_array(op_array, ssa, call_map)) {
|
||||
remove_nops = 1;
|
||||
if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
|
||||
if (sccp_optimize_op_array(op_array, ssa, call_map)) {
|
||||
remove_nops = 1;
|
||||
}
|
||||
if (ZEND_FUNC_INFO(op_array)) {
|
||||
if (zend_dfa_optimize_calls(op_array, ssa)) {
|
||||
remove_nops = 1;
|
||||
}
|
||||
}
|
||||
if (ctx->debug_level & ZEND_DUMP_AFTER_SCCP_PASS) {
|
||||
zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after sccp pass", ssa);
|
||||
}
|
||||
}
|
||||
|
||||
for (v = op_array->last_var; v < ssa->vars_count; v++) {
|
||||
|
@ -581,122 +711,6 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
|
|||
}
|
||||
}
|
||||
|
||||
if (ZEND_FUNC_INFO(op_array)) {
|
||||
zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
|
||||
|
||||
if (func_info->callee_info) {
|
||||
zend_call_info *call_info = func_info->callee_info;
|
||||
static zend_function *in_array_function = NULL;
|
||||
|
||||
if (!in_array_function) {
|
||||
in_array_function = zend_hash_str_find_ptr(CG(function_table), "in_array", sizeof("in_array")-1);
|
||||
}
|
||||
do {
|
||||
if (call_info->callee_func == in_array_function
|
||||
&& (call_info->caller_init_opline->extended_value == 2
|
||||
|| (call_info->caller_init_opline->extended_value == 3
|
||||
&& (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
|
||||
&& (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
|
||||
|
||||
zend_op *send_array;
|
||||
zend_op *send_needly;
|
||||
zend_bool strict = 0;
|
||||
|
||||
if (call_info->caller_init_opline->extended_value == 2) {
|
||||
send_array = call_info->caller_call_opline - 1;
|
||||
send_needly = call_info->caller_call_opline - 2;
|
||||
} else {
|
||||
if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
|
||||
strict = 1;
|
||||
}
|
||||
send_array = call_info->caller_call_opline - 2;
|
||||
send_needly = call_info->caller_call_opline - 3;
|
||||
}
|
||||
|
||||
if (send_array->opcode == ZEND_SEND_VAL
|
||||
&& send_array->op1_type == IS_CONST
|
||||
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
|
||||
&& (send_needly->opcode == ZEND_SEND_VAL
|
||||
|| send_needly->opcode == ZEND_SEND_VAR)
|
||||
) {
|
||||
int ok = 1;
|
||||
|
||||
HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
|
||||
HashTable *dst;
|
||||
zval *val, tmp;
|
||||
zend_ulong idx;
|
||||
|
||||
ZVAL_TRUE(&tmp);
|
||||
dst = emalloc(sizeof(HashTable));
|
||||
zend_hash_init(dst, zend_hash_num_elements(src), NULL, ZVAL_PTR_DTOR, 0);
|
||||
if (strict) {
|
||||
ZEND_HASH_FOREACH_VAL(src, val) {
|
||||
if (Z_TYPE_P(val) == IS_STRING) {
|
||||
zend_hash_add(dst, Z_STR_P(val), &tmp);
|
||||
} else if (Z_TYPE_P(val) == IS_LONG) {
|
||||
zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
|
||||
} else {
|
||||
zend_array_destroy(dst);
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
} else {
|
||||
ZEND_HASH_FOREACH_VAL(src, val) {
|
||||
if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
|
||||
zend_array_destroy(dst);
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
zend_hash_add(dst, Z_STR_P(val), &tmp);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
uint32_t op_num = send_needly - op_array->opcodes;
|
||||
zend_ssa_op *ssa_op = ssa->ops + op_num;
|
||||
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
/* Reconstruct SSA */
|
||||
int var_num = ssa_op->op1_use;
|
||||
zend_ssa_var *var = ssa->vars + var_num;
|
||||
|
||||
ZEND_ASSERT(ssa_op->op1_def < 0);
|
||||
zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
|
||||
ssa_op->op1_use = -1;
|
||||
ssa_op->op1_use_chain = -1;
|
||||
op_num = call_info->caller_call_opline - op_array->opcodes;
|
||||
ssa_op = ssa->ops + op_num;
|
||||
ssa_op->op1_use = var_num;
|
||||
ssa_op->op1_use_chain = var->use_chain;
|
||||
var->use_chain = op_num;
|
||||
}
|
||||
|
||||
ZVAL_ARR(&tmp, dst);
|
||||
|
||||
/* Update opcode */
|
||||
call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
|
||||
call_info->caller_call_opline->extended_value = strict;
|
||||
call_info->caller_call_opline->op1_type = send_needly->op1_type;
|
||||
call_info->caller_call_opline->op1.num = send_needly->op1.num;
|
||||
call_info->caller_call_opline->op2_type = IS_CONST;
|
||||
call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
|
||||
if (call_info->caller_init_opline->extended_value == 3) {
|
||||
MAKE_NOP(call_info->caller_call_opline - 1);
|
||||
}
|
||||
MAKE_NOP(call_info->caller_init_opline);
|
||||
MAKE_NOP(send_needly);
|
||||
MAKE_NOP(send_array);
|
||||
remove_nops = 1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
call_info = call_info->next_callee;
|
||||
} while (call_info);
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_nops) {
|
||||
zend_ssa_remove_nops(op_array, ssa);
|
||||
}
|
||||
|
|
|
@ -1280,9 +1280,13 @@ static int replace_constant_operands(sccp_ctx *ctx) {
|
|||
&& var->phi_use_chain == NULL) {
|
||||
if (opline->opcode == ZEND_DO_ICALL) {
|
||||
/* Call instruction -> remove opcodes that are part of the call */
|
||||
zend_call_info *call = ctx->call_map[var->definition];
|
||||
zend_call_info *call;
|
||||
int i;
|
||||
|
||||
ZEND_ASSERT(ctx->call_map);
|
||||
call = ctx->call_map[var->definition];
|
||||
ZEND_ASSERT(call);
|
||||
ZEND_ASSERT(call->caller_call_opline == opline);
|
||||
zend_ssa_remove_result_def(ssa, ssa_op);
|
||||
zend_ssa_remove_instr(ssa, opline, ssa_op);
|
||||
zend_ssa_remove_instr(ssa, call->caller_init_opline,
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_8 (1<<7)
|
||||
#define ZEND_OPTIMIZER_PASS_8 (1<<7) /* SCCP (constant propagation) */
|
||||
#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
|
||||
#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
|
||||
#define ZEND_OPTIMIZER_PASS_11 (1<<10) /* Merge equal constants */
|
||||
|
@ -77,6 +77,7 @@
|
|||
#define ZEND_DUMP_DFA_PHI (1<<26)
|
||||
#define ZEND_DUMP_DFA_SSA (1<<27)
|
||||
#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
|
||||
#define ZEND_DUMP_AFTER_SCCP_PASS (1<<29)
|
||||
|
||||
typedef struct _zend_script {
|
||||
zend_string *filename;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue