Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
# include "ssa_integrity.c"
#endif

zend_result zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
zend_result zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *arg_types, HashTable *op_array_to_arg_offset)
{
uint32_t build_flags;

Expand Down Expand Up @@ -95,7 +95,7 @@ zend_result zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ct

zend_ssa_find_sccs(op_array, ssa);

if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa, ctx->optimization_level) == FAILURE) {
if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa, ctx->optimization_level, arg_types, op_array_to_arg_offset) == FAILURE) {
return FAILURE;
}

Expand Down Expand Up @@ -1708,7 +1708,7 @@ void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
void *checkpoint = zend_arena_checkpoint(ctx->arena);
zend_ssa ssa;

if (zend_dfa_analyze_op_array(op_array, ctx, &ssa) == FAILURE) {
if (zend_dfa_analyze_op_array(op_array, ctx, &ssa, NULL, 0) == FAILURE) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
Expand Down
59 changes: 58 additions & 1 deletion Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -4462,7 +4462,7 @@ static void zend_mark_cv_references(const zend_op_array *op_array, const zend_sc
free_alloca(worklist, use_heap);
}

ZEND_API zend_result zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) /* {{{ */
ZEND_API zend_result zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level, uint32_t *arg_types, HashTable *op_array_to_arg_offset) /* {{{ */
{
zend_ssa_var_info *ssa_var_info;
int i;
Expand Down Expand Up @@ -4499,6 +4499,63 @@ ZEND_API zend_result zend_ssa_inference(zend_arena **arena, const zend_op_array
return FAILURE;
}

if (arg_types && op_array->scope) {
const zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
if (func_info) {
for (zend_call_info *callee_info = func_info->callee_info; callee_info != NULL; callee_info = callee_info->next_callee) {
const zend_op_array *callee_op_array = &callee_info->callee_func->op_array;

/* Only support argument type inference for user functions. */
if (callee_op_array->type != ZEND_USER_FUNCTION) {
continue;
}
/* Private functions can only be called from the class itself, and cannot be overridden.
* Therefore, we know which argument types they can receive at compile time. */
if (!(callee_op_array->fn_flags & ZEND_ACC_PRIVATE)) {
continue;
}

const zval *arg_types_offset_zval = zend_hash_index_find(op_array_to_arg_offset, (zend_ulong) callee_op_array);
uint32_t arg_types_offset = Z_LVAL_P(arg_types_offset_zval);

/* Too complicated to handle here for now. */
if (callee_info->named_args || callee_info->num_args < callee_op_array->num_args || (callee_op_array->fn_flags & ZEND_ACC_VARIADIC)) {
/* If we already started inferring, clear those types. */
memset(arg_types + arg_types_offset, -1, callee_op_array->num_args * sizeof(uint32_t));
continue;
}
// fprintf(stderr, "callee info %p, %s\n", callee_info, callee_info->callee_func->common.function_name->val);

bool all_by_val = true;
for (uint32_t arg = 0; arg < callee_op_array->num_args && all_by_val; arg++) {
const zend_op *opline = callee_info->arg_info[arg].opline;
if (opline->opcode != ZEND_SEND_VAR && opline->opcode != ZEND_SEND_VAL) {
all_by_val = false;
}
}

if (all_by_val) {
for (uint32_t arg = 0; arg < callee_op_array->num_args; arg++) {
const zend_op *opline = callee_info->arg_info[arg].opline;
const zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
uint32_t type;
if (opline->op1_type == IS_CONST) {
type = 1 << Z_TYPE_P(CRT_CONSTANT(opline->op1));
} else {
/* Note: if this has an op1_def, it's for refcounting purposes. */
ZEND_ASSERT(ssa_op->op1_use > -1);
type = ssa->var_info[ssa_op->op1_use].type & _ZEND_TYPE_MAY_BE_MASK;
}
arg_types[arg_types_offset + arg] |= type;
}
} else {
/* If we already started inferring, clear those types. */
memset(arg_types + arg_types_offset, -1, callee_op_array->num_args * sizeof(uint32_t));
}
}
}
}

return SUCCESS;
}
/* }}} */
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/zend_inference.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ BEGIN_EXTERN_C()

ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
ZEND_API zend_result zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level);
ZEND_API zend_result zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level, uint32_t *arg_types, HashTable *op_array_to_arg_offset);

ZEND_API uint32_t zend_array_element_type(uint32_t t1, uint8_t op_type, int write, int insert);

Expand Down
61 changes: 55 additions & 6 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,18 @@ static void zend_optimizer_call_registered_passes(zend_script *script, void *ctx
}
}

static void zend_dfa_analysis_with_call_graph(zend_op_array *op_array, zend_optimizer_ctx *ctx, uint32_t *arg_types, HashTable *op_array_to_arg_offset)
{
zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
if (func_info) {
if (zend_dfa_analyze_op_array(op_array, ctx, &func_info->ssa, arg_types, op_array_to_arg_offset) == SUCCESS) {
func_info->flags = func_info->ssa.cfg.flags;
} else {
ZEND_SET_FUNC_INFO(op_array, NULL);
}
}
}

ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
{
zend_op_array *op_array;
Expand Down Expand Up @@ -1505,17 +1517,54 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
}
}

/* Group all non-private methods first, they will be analyzed first, and their type inference info can be used
* for private method arguments. */
int num_non_private_methods = 0;
uint32_t total_arg_count = 0;
zend_op_array **op_array_grouped = zend_arena_alloc(&ctx.arena, sizeof(zend_op_array *) * call_graph.op_arrays_count);
HashTable *op_array_to_arg_offset = zend_arena_alloc(&ctx.arena, sizeof(HashTable));
zend_hash_init(op_array_to_arg_offset, call_graph.op_arrays_count, NULL, NULL, false);
for (i = 0; i < call_graph.op_arrays_count; i++) {
func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
if (func_info) {
if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) {
func_info->flags = func_info->ssa.cfg.flags;
} else {
ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
if (!(call_graph.op_arrays[i]->fn_flags & ZEND_ACC_PRIVATE)) {
num_non_private_methods++;
}
zval tmp;
ZVAL_LONG(&tmp, total_arg_count);
zend_hash_index_add_new(op_array_to_arg_offset, (zend_ulong) call_graph.op_arrays[i], &tmp);
total_arg_count += call_graph.op_arrays[i]->num_args;
}
uint32_t *arg_types = zend_arena_calloc(&ctx.arena, total_arg_count, sizeof(uint32_t));
int start_private_method_idx = num_non_private_methods;
int current_private_method_idx = start_private_method_idx;
for (i = 0; i < call_graph.op_arrays_count; i++) {
if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_PRIVATE) {
op_array_grouped[current_private_method_idx++] = call_graph.op_arrays[i];
} else {
op_array_grouped[--num_non_private_methods] = call_graph.op_arrays[i];
}
}

for (i = 0; i < start_private_method_idx; i++) {
zend_dfa_analysis_with_call_graph(op_array_grouped[i], &ctx, arg_types, op_array_to_arg_offset);
}

for (i = start_private_method_idx; i < call_graph.op_arrays_count; i++) {
zend_op_array *op_array = op_array_grouped[i];
const zval *arg_types_offset_zval = zend_hash_index_find(op_array_to_arg_offset, (zend_ulong) op_array);
uint32_t arg_types_offset = Z_LVAL_P(arg_types_offset_zval);

for (uint32_t arg = 0; arg < op_array->num_args; arg++) {
uint32_t inferred_type = arg_types[arg_types_offset + arg];
if (inferred_type != -1 && !ZEND_TYPE_IS_SET(op_array->arg_info[arg].type)) {
op_array->arg_info[arg].type.type_mask |= inferred_type;
}
}

zend_dfa_analysis_with_call_graph(op_array, &ctx, arg_types, op_array_to_arg_offset);
}

zend_hash_destroy(op_array_to_arg_offset);

//TODO: perform inner-script inference???
for (i = 0; i < call_graph.op_arrays_count; i++) {
func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/zend_optimizer_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx);
zend_result zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa);
zend_result zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *arg_types, HashTable *op_array_to_arg_offset);
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map);
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx);
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/jit/zend_jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,7 @@ static int zend_jit_op_array_analyze2(const zend_op_array *op_array, zend_script
&& !(op_array->fn_flags & ZEND_ACC_GENERATOR)
&& !(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
if (zend_ssa_inference(&CG(arena), op_array, script, ssa,
optimization_level & ~ZEND_OPTIMIZER_NARROW_TO_DOUBLE) != SUCCESS) {
optimization_level & ~ZEND_OPTIMIZER_NARROW_TO_DOUBLE, NULL, 0) != SUCCESS) {
return FAILURE;
}
}
Expand Down