Added e-SSA based DFA optimisation framework (incomplete)

This commit is contained in:
Dmitry Stogov 2015-12-16 00:49:44 +03:00
parent 8d6d28d9c6
commit c88ffa9a56
21 changed files with 7085 additions and 188 deletions

View file

@ -1745,13 +1745,13 @@ void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
/* Build CFG */
checkpoint = zend_arena_checkpoint(ctx->arena);
if (zend_build_cfg(&ctx->arena, op_array, 0, 0, &cfg, NULL) != SUCCESS) {
if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg, NULL) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
zend_dump_op_array(op_array, &cfg, ZEND_DUMP_UNREACHABLE, "before block pass");
zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
}
if (op_array->last_var || op_array->T) {
@ -1805,7 +1805,7 @@ void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
assemble_code_blocks(&cfg, op_array);
if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
zend_dump_op_array(op_array, &cfg, 0, "after block pass");
zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
}
/* Destroy CFG */

View file

@ -26,6 +26,8 @@
#include "zend_bitset.h"
#include "zend_cfg.h"
#include "zend_ssa.h"
#include "zend_func_info.h"
#include "zend_inference.h"
#include "zend_dump.h"
#ifndef HAVE_DFA_PASS
@ -35,8 +37,8 @@
void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
void *checkpoint;
uint32_t flags;
zend_cfg cfg;
uint32_t build_flags;
uint32_t flags = 0;
zend_ssa ssa;
#if !HAVE_DFA_PASS
@ -44,9 +46,10 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
#endif
/* Build SSA */
memset(&ssa, 0, sizeof(ssa));
checkpoint = zend_arena_checkpoint(ctx->arena);
if (zend_build_cfg(&ctx->arena, op_array, 0, 0, &cfg, &flags) != SUCCESS) {
if (zend_build_cfg(&ctx->arena, op_array, 0, &ssa.cfg, &flags) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
@ -56,52 +59,79 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
return;
}
if (zend_cfg_build_predecessors(&ctx->arena, &cfg) != SUCCESS) {
if (zend_cfg_build_predecessors(&ctx->arena, &ssa.cfg) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
zend_dump_op_array(op_array, &cfg, 0, "dfa cfg");
zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNUSED_VARS, "dfa cfg", &ssa.cfg);
}
/* Compute Dominators Tree */
if (zend_cfg_compute_dominators_tree(op_array, &cfg) != SUCCESS) {
if (zend_cfg_compute_dominators_tree(op_array, &ssa.cfg) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
/* Identify reducible and irreducible loops */
if (zend_cfg_identify_loops(op_array, &cfg, &flags) != SUCCESS) {
if (zend_cfg_identify_loops(op_array, &ssa.cfg, &flags) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
int j;
fprintf(stderr, "DOMINATORS-TREE:\n");
for (j = 0; j < cfg.blocks_count; j++) {
zend_basic_block *b = cfg.blocks + j;
if (b->flags & ZEND_BB_REACHABLE) {
zend_dump_block_info(&cfg, j, 0);
}
}
zend_dump_dominators(op_array, &ssa.cfg);
}
if (zend_build_ssa(&ctx->arena, op_array, &cfg, 0, &ssa, &flags) != SUCCESS) {
build_flags = 0;
if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) {
build_flags |= ZEND_SSA_DEBUG_LIVENESS;
}
if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
}
if (zend_build_ssa(&ctx->arena, op_array, build_flags, &ssa, &flags) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
zend_dump_op_array(op_array, &cfg, ZEND_DUMP_UNREACHABLE, "before dfa pass");
if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "before dfa pass", &ssa);
}
//TODO: ???
if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, &ssa) != SUCCESS){
zend_arena_release(&ctx->arena, checkpoint);
return;
}
if (zend_ssa_find_false_dependencies(op_array, &ssa) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
if (zend_ssa_find_sccs(op_array, &ssa) != SUCCESS){
zend_arena_release(&ctx->arena, checkpoint);
return;
}
if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, &ssa) != SUCCESS) {
return;
}
if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
zend_dump_ssa_variables(op_array, &ssa);
}
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "before dfa pass", &ssa);
}
//TODO: Add optimization???
if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
zend_dump_op_array(op_array, &cfg, 0, "after dfa pass");
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "after dfa pass", &ssa);
}
/* Destroy SSA */

View file

@ -0,0 +1,304 @@
/*
+----------------------------------------------------------------------+
| Zend Engine, Call Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2015 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
/* $Id:$ */
#include "php.h"
#include "zend_compile.h"
#include "zend_extensions.h"
#include "zend_inference.h"
#include "zend_call_graph.h"
#include "zend_func_info.h"
#include "zend_inference.h"
#include "zend_call_graph.h"
typedef int (*zend_op_array_func_t)(zend_call_graph *call_graph, zend_op_array *op_array);
static int zend_op_array_calc(zend_call_graph *call_graph, zend_op_array *op_array)
{
(void) op_array;
call_graph->op_arrays_count++;
return SUCCESS;
}
static int zend_op_array_collect(zend_call_graph *call_graph, zend_op_array *op_array)
{
zend_func_info *func_info = call_graph->func_infos + call_graph->op_arrays_count;
ZEND_SET_FUNC_INFO(op_array, func_info);
call_graph->op_arrays[call_graph->op_arrays_count] = op_array;
func_info->num = call_graph->op_arrays_count;
func_info->num_args = -1;
func_info->return_value_used = -1;
call_graph->op_arrays_count++;
return SUCCESS;
}
static int zend_foreach_op_array(zend_call_graph *call_graph, zend_script *script, zend_op_array_func_t func)
{
zend_class_entry *ce;
zend_op_array *op_array;
if (func(call_graph, &script->main_op_array) != SUCCESS) {
return FAILURE;
}
ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
if (func(call_graph, op_array) != SUCCESS) {
return FAILURE;
}
} ZEND_HASH_FOREACH_END();
ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
if (op_array->scope == ce) {
if (func(call_graph, op_array) != SUCCESS) {
return FAILURE;
}
}
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FOREACH_END();
return SUCCESS;
}
static void zend_collect_args_info(zend_call_info *call_info)
{
zend_op *opline = call_info->caller_init_opline;
zend_op *end = call_info->caller_call_opline;
uint32_t i;
int num;
int level = 0;
ZEND_ASSERT(opline && end);
if (!opline->extended_value) {
return;
}
for (i = 0; i < opline->extended_value; i++) {
call_info->arg_info[i].opline = NULL;
}
while (opline < end) {
opline++;
switch (opline->opcode) {
case ZEND_SEND_VAL:
case ZEND_SEND_VAR:
case ZEND_SEND_VAL_EX:
case ZEND_SEND_VAR_EX:
case ZEND_SEND_REF:
case ZEND_SEND_VAR_NO_REF:
num = opline->op2.num;
if (num > 0) {
num--;
}
if (!level) {
call_info->arg_info[num].opline = opline;
}
break;
case ZEND_SEND_ARRAY:
case ZEND_SEND_USER:
case ZEND_SEND_UNPACK:
// ???
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_INIT_DYNAMIC_CALL:
case ZEND_NEW:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_USER_CALL:
level++;
break;
case ZEND_DO_FCALL:
case ZEND_DO_ICALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
level--;
break;
}
}
}
static int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info)
{
zend_op *opline = op_array->opcodes;
zend_op *end = opline + op_array->last;
zend_function *func;
zend_call_info *call_info;
int call = 0;
zend_call_info **call_stack = alloca((op_array->last / 2) * sizeof(zend_call_info*));
while (opline != end) {
call_info = NULL;
switch (opline->opcode) {
case ZEND_INIT_FCALL:
if ((func = zend_hash_find_ptr(&script->function_table, Z_STR_P(CRT_CONSTANT(opline->op2)))) != NULL) {
zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array);
if (callee_func_info) {
call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
call_info->caller_op_array = op_array;
call_info->caller_init_opline = opline;
call_info->caller_call_opline = NULL;
call_info->callee_func = func;
call_info->num_args = opline->extended_value;
call_info->next_caller = callee_func_info->caller_info;
callee_func_info->caller_info = call_info;
call_info->next_callee = func_info->callee_info;
func_info->callee_info = call_info;
}
} else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(CRT_CONSTANT(opline->op2)))) != NULL &&
func->type == ZEND_INTERNAL_FUNCTION) {
call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
call_info->caller_op_array = op_array;
call_info->caller_init_opline = opline;
call_info->caller_call_opline = NULL;
call_info->callee_func = func;
call_info->num_args = opline->extended_value;
call_info->next_caller = NULL;
call_info->next_callee = func_info->callee_info;
func_info->callee_info = call_info;
}
/* break missing intentionally */
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
case ZEND_INIT_DYNAMIC_CALL:
case ZEND_NEW:
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_USER_CALL:
call_stack[call] = call_info;
call++;
break;
case ZEND_DO_FCALL:
case ZEND_DO_ICALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
func_info->flags |= ZEND_FUNC_HAS_CALLS;
call--;
if (call_stack[call]) {
call_stack[call]->caller_call_opline = opline;
zend_collect_args_info(call_stack[call]);
}
break;
}
opline++;
}
return SUCCESS;
}
static int zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited)
{
zend_func_info *func_info;
zend_call_info *call_info;
int ret = 0;
if (op_array == root) {
return 1;
}
func_info = ZEND_FUNC_INFO(op_array);
if (zend_bitset_in(visited, func_info->num)) {
return 0;
}
zend_bitset_incl(visited, func_info->num);
call_info = func_info->caller_info;
while (call_info) {
if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) {
call_info->recursive = 1;
ret = 1;
}
call_info = call_info->next_caller;
}
return ret;
}
static void zend_analyze_recursion(zend_call_graph *call_graph)
{
zend_op_array *op_array;
zend_func_info *func_info;
zend_call_info *call_info;
int i;
int set_len = zend_bitset_len(call_graph->op_arrays_count);
zend_bitset visited;
ALLOCA_FLAG(use_heap);
visited = ZEND_BITSET_ALLOCA(set_len, use_heap);
for (i = 0; i < call_graph->op_arrays_count; i++) {
op_array = call_graph->op_arrays[i];
func_info = call_graph->func_infos + i;
call_info = func_info->caller_info;
while (call_info) {
if (call_info->caller_op_array == op_array) {
call_info->recursive = 1;
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY;
} else {
memset(visited, 0, sizeof(uint32_t) * set_len);
if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) {
call_info->recursive = 1;
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY;
}
}
call_info = call_info->next_caller;
}
}
}
static void zend_sort_op_arrays(zend_call_graph *call_graph)
{
(void) call_graph;
// TODO: perform topological sort of cyclic call graph
}
int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph) /* {{{ */
{
int i;
call_graph->op_arrays_count = 0;
if (zend_foreach_op_array(call_graph, script, zend_op_array_calc) != SUCCESS) {
return FAILURE;
}
call_graph->op_arrays = (zend_op_array**)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_op_array*));
call_graph->func_infos = (zend_func_info*)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_func_info));
if (!call_graph->op_arrays || !call_graph->func_infos) {
return FAILURE;
}
call_graph->op_arrays_count = 0;
if (zend_foreach_op_array(call_graph, script, zend_op_array_collect) != SUCCESS) {
return FAILURE;
}
for (i = 0; i < call_graph->op_arrays_count; i++) {
zend_analyze_calls(arena, script, build_flags, call_graph->op_arrays[i], call_graph->func_infos + i);
}
zend_analyze_recursion(call_graph);
zend_sort_op_arrays(call_graph);
return SUCCESS;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/

View file

@ -0,0 +1,83 @@
/*
+----------------------------------------------------------------------+
| Zend Engine, Call Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2015 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_CALL_GRAPH_H
#define ZEND_CALL_GRAPH_H
#include "zend_ssa.h"
#include "zend_func_info.h"
#include "zend_optimizer.h"
typedef struct _zend_send_arg_info {
zend_op *opline;
} zend_send_arg_info;
typedef struct _zend_recv_arg_info {
int ssa_var;
zend_ssa_var_info info;
} zend_recv_arg_info;
struct _zend_call_info {
zend_op_array *caller_op_array;
zend_op *caller_init_opline;
zend_op *caller_call_opline;
zend_function *callee_func;
zend_call_info *next_caller;
zend_call_info *next_callee;
zend_func_info *clone;
int recursive;
int num_args;
zend_send_arg_info arg_info[1];
};
struct _zend_func_info {
int num;
uint32_t flags;
zend_ssa ssa; /* Static Single Assignmnt Form */
zend_call_info *caller_info; /* where this function is called from */
zend_call_info *callee_info; /* which functions are called from this one */
int num_args; /* (-1 - unknown) */
zend_recv_arg_info *arg_info;
zend_ssa_var_info return_info;
zend_func_info *clone;
int clone_num;
int return_value_used; /* -1 unknown, 0 no, 1 yes */
void *codegen_data;
};
typedef struct _zend_call_graph {
int op_arrays_count;
zend_op_array **op_arrays;
zend_func_info *func_infos;
} zend_call_graph;
BEGIN_EXTERN_C()
int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph);
END_EXTERN_C()
#endif /* ZEND_CALL_GRAPH_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/

View file

@ -19,6 +19,7 @@
#include "php.h"
#include "zend_compile.h"
#include "zend_cfg.h"
#include "zend_func_info.h"
#include "zend_worklist.h"
static void zend_mark_reachable(zend_op *opcodes, zend_basic_block *blocks, zend_basic_block *b) /* {{{ */
@ -75,7 +76,7 @@ static void zend_mark_reachable(zend_op *opcodes, zend_basic_block *blocks, zend
}
/* }}} */
static void zend_mark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
@ -199,7 +200,7 @@ static void zend_mark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg, i
}
/* }}} */
void zend_cfg_remark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
int i;
@ -232,7 +233,7 @@ static void record_successor(zend_basic_block *blocks, int pred, int n, int succ
block_map[i] = 1; \
} while (0)
int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants, int stackless, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */
{
uint32_t flags = 0;
uint32_t i;
@ -270,7 +271,7 @@ int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants
case ZEND_YIELD:
case ZEND_YIELD_FROM:
flags |= ZEND_FUNC_TOO_DYNAMIC;
if (stackless) {
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
@ -278,7 +279,7 @@ int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
flags |= ZEND_FUNC_HAS_CALLS;
if (stackless) {
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
@ -532,7 +533,7 @@ int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants
zend_mark_reachable_blocks(op_array, cfg, 0);
if (func_flags) {
*func_flags = flags;
*func_flags |= flags;
}
return SUCCESS;
@ -602,7 +603,7 @@ int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
}
/* }}} */
int zend_cfg_compute_dominators_tree(zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
int blocks_count = cfg->blocks_count;
@ -694,7 +695,7 @@ static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
}
/* }}} */
int zend_cfg_identify_loops(zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */
int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */
{
int i, j, k;
int depth;

View file

@ -19,13 +19,6 @@
#ifndef ZEND_CFG_H
#define ZEND_CFG_H
/* func flags */
#define ZEND_FUNC_TOO_DYNAMIC (1<<0)
#define ZEND_FUNC_HAS_CALLS (1<<1)
#define ZEND_FUNC_VARARG (1<<2)
#define ZEND_FUNC_NO_LOOPS (1<<3)
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
/* zend_basic_bloc.flags */
#define ZEND_BB_START (1<<0) /* fist block */
#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
@ -95,20 +88,32 @@ typedef struct _zend_cfg {
uint32_t *map;
} zend_cfg;
#define CRT_CONSTANT(node) \
(rt_constants ? \
/* Build Flags */
#define ZEND_RT_CONSTANTS (1<<31)
#define ZEND_CFG_STACKLESS (1<<30)
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
#define CRT_CONSTANT_EX(op_array, node, rt_constants) \
((rt_constants) ? \
RT_CONSTANT(op_array, (node)) \
: \
CT_CONSTANT_EX(op_array, (node).constant) \
)
#define CRT_CONSTANT(node) \
CRT_CONSTANT_EX(op_array, node, (build_flags & ZEND_RT_CONSTANTS))
#define RETURN_VALUE_USED(opline) \
(!((opline)->result_type & EXT_TYPE_UNUSED))
BEGIN_EXTERN_C()
int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants, int stackless, zend_cfg *cfg, uint32_t *func_flags);
void zend_cfg_remark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg);
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags);
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
int zend_cfg_compute_dominators_tree(zend_op_array *op_array, zend_cfg *cfg);
int zend_cfg_identify_loops(zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags);
int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags);
END_EXTERN_C()

View file

@ -20,7 +20,7 @@
#include "zend_compile.h"
#include "zend_dfg.h"
int zend_build_dfg(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg) /* {{{ */
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg) /* {{{ */
{
int set_size;
zend_basic_block *blocks = cfg->blocks;
@ -223,18 +223,6 @@ int zend_build_dfg(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg) /* {{{
}
} while (changed);
//???D if (ZCG(accel_directives).jit_debug & JIT_DEBUG_DUMP_LIVENESS) {
//???D fprintf(stderr, "Variable Liveness\n");
//???D for (j = 0; j < blocks_count; j++) {
//???D fprintf(stderr, " BB%d:\n", j);
//???D zend_jit_dump_var_set(op_array, "gen", dfg->gen + (j * dfg->size));
//???D zend_jit_dump_var_set(op_array, "def", dfg->def + (j * dfg->size));
//???D zend_jit_dump_var_set(op_array, "use", dfg->use + (j * dfg->size));
//???D zend_jit_dump_var_set(op_array, "in ", dfg->in + (j * dfg->size));
//???D zend_jit_dump_var_set(op_array, "out", dfg->out + (j * dfg->size));
//???D }
//???D }
return SUCCESS;
}
/* }}} */

View file

@ -44,7 +44,7 @@ typedef struct _zend_dfg {
BEGIN_EXTERN_C()
int zend_build_dfg(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg);
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg);
END_EXTERN_C()

View file

@ -19,6 +19,10 @@
#include "php.h"
#include "zend_compile.h"
#include "zend_cfg.h"
#include "zend_ssa.h"
#include "zend_inference.h"
#include "zend_func_info.h"
#include "zend_call_graph.h"
#include "zend_dump.h"
static void zend_dump_const(const zval *zv)
@ -46,7 +50,7 @@ static void zend_dump_const(const zval *zv)
fprintf(stderr, " array(...)");
break;
default:
fprintf(stderr, " ???");
fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
break;
}
}
@ -84,17 +88,311 @@ static void zend_dump_class_fetch_type(uint32_t fetch_type)
}
}
static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline)
void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num)
{
if (var_type == IS_CV && var_num < op_array->last_var) {
fprintf(stderr, "CV%d($%s)", var_num, op_array->vars[var_num]->val);
} else if (var_type == IS_VAR) {
fprintf(stderr, "V%d", var_num);
} else if (var_type == IS_TMP_VAR) {
fprintf(stderr, "T%d", var_num);
} else {
fprintf(stderr, "X%d", var_num);
}
}
static void zend_dump_range(const zend_ssa_range *r)
{
if (r->underflow && r->overflow) {
return;
}
fprintf(stderr, " RANGE[");
if (r->underflow) {
fprintf(stderr, "--..");
} else {
fprintf(stderr, ZEND_LONG_FMT "..", r->min);
}
if (r->overflow) {
fprintf(stderr, "++]");
} else {
fprintf(stderr, ZEND_LONG_FMT "]", r->max);
}
}
static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof)
{
int first = 1;
fprintf(stderr, " [");
if (info & MAY_BE_UNDEF) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "undef");
}
if (info & MAY_BE_DEF) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "def");
}
if (info & MAY_BE_REF) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "ref");
}
if (info & MAY_BE_RC1) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "rc1");
}
if (info & MAY_BE_RCN) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "rcn");
}
if (info & MAY_BE_CLASS) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "class");
if (ce) {
if (is_instanceof) {
fprintf(stderr, " (instanceof %s)", ce->name->val);
} else {
fprintf(stderr, " (%s)", ce->name->val);
}
}
} else if ((info & MAY_BE_ANY) == MAY_BE_ANY) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "any");
} else {
if (info & MAY_BE_NULL) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "null");
}
if (info & MAY_BE_FALSE) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "false");
}
if (info & MAY_BE_TRUE) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "true");
}
if (info & MAY_BE_LONG) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "long");
}
if (info & MAY_BE_DOUBLE) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "double");
}
if (info & MAY_BE_STRING) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "string");
}
if (info & MAY_BE_ARRAY) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "array");
if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 &&
(info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY) {
int afirst = 1;
fprintf(stderr, " [");
if (info & MAY_BE_ARRAY_KEY_LONG) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "long");
}
if (info & MAY_BE_ARRAY_KEY_STRING) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "string");
}
fprintf(stderr, "]");
}
if (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)) {
int afirst = 1;
fprintf(stderr, " of [");
if ((info & MAY_BE_ARRAY_OF_ANY) == MAY_BE_ARRAY_OF_ANY) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "any");
} else {
if (info & MAY_BE_ARRAY_OF_NULL) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "null");
}
if (info & MAY_BE_ARRAY_OF_FALSE) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "false");
}
if (info & MAY_BE_ARRAY_OF_TRUE) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "true");
}
if (info & MAY_BE_ARRAY_OF_LONG) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "long");
}
if (info & MAY_BE_ARRAY_OF_DOUBLE) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "double");
}
if (info & MAY_BE_ARRAY_OF_STRING) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "string");
}
if (info & MAY_BE_ARRAY_OF_ARRAY) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "array");
}
if (info & MAY_BE_ARRAY_OF_OBJECT) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "object");
}
if (info & MAY_BE_ARRAY_OF_RESOURCE) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "resource");
}
}
if (info & MAY_BE_ARRAY_OF_REF) {
if (afirst) afirst = 0; else fprintf(stderr, ", ");
fprintf(stderr, "ref");
}
fprintf(stderr, "]");
}
}
if (info & MAY_BE_OBJECT) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "object");
if (ce) {
if (is_instanceof) {
fprintf(stderr, " (instanceof %s)", ce->name->val);
} else {
fprintf(stderr, " (%s)", ce->name->val);
}
}
}
if (info & MAY_BE_RESOURCE) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "resource");
}
}
if (info & MAY_BE_ERROR) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "error");
}
//TODO: this is useful only for JIT???
if (info & MAY_BE_IN_REG) {
if (first) first = 0; else fprintf(stderr, ", ");
fprintf(stderr, "reg");
}
fprintf(stderr, "]");
}
static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
{
zend_dump_type_info(
ssa->var_info[ssa_var_num].type,
ssa->var_info[ssa_var_num].ce,
ssa->var_info[ssa_var_num].ce ?
ssa->var_info[ssa_var_num].is_instanceof : 0);
}
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)
{
if (ssa_var_num >= 0) {
fprintf(stderr, "#%d.", ssa_var_num);
} else {
fprintf(stderr, "#?.");
}
zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num);
if (ssa_var_num >= 0 && ssa->vars) {
if (ssa_var_num >= 0 && ssa->vars[ssa_var_num].no_val) {
fprintf(stderr, " NOVAL");
}
if (ssa->var_info) {
zend_dump_ssa_var_info(ssa, ssa_var_num);
if (ssa->var_info[ssa_var_num].has_range) {
zend_dump_range(&ssa->var_info[ssa_var_num].range);
}
}
}
}
static void zend_dump_pi_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_pi_range *r)
{
if (r->range.underflow && r->range.overflow) {
return;
}
fprintf(stderr, " RANGE");
if (r->negative) {
fprintf(stderr, "~");
}
fprintf(stderr, "[");
if (r->range.underflow) {
fprintf(stderr, "-- .. ");
} else {
if (r->min_ssa_var >= 0) {
zend_dump_ssa_var(op_array, ssa, r->min_ssa_var, (r->min_var < op_array->last_var ? IS_CV : 0), r->min_var);
if (r->range.min > 0) {
fprintf(stderr, " + " ZEND_LONG_FMT, r->range.min);
} else if (r->range.min < 0) {
fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.min);
}
fprintf(stderr, " .. ");
} else {
fprintf(stderr, ZEND_LONG_FMT " .. ", r->range.min);
}
}
if (r->range.overflow) {
fprintf(stderr, "++]");
} else {
if (r->max_ssa_var >= 0) {
zend_dump_ssa_var(op_array, ssa, r->max_ssa_var, (r->max_var < op_array->last_var ? IS_CV : 0), r->max_var);
if (r->range.max > 0) {
fprintf(stderr, " + " ZEND_LONG_FMT, r->range.max);
} else if (r->range.max < 0) {
fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.max);
}
fprintf(stderr, "]");
} else {
fprintf(stderr, ZEND_LONG_FMT "]", r->range.max);
}
}
}
static 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)
{
const char *name = zend_get_opcode_name(opline->opcode);
uint32_t flags = zend_get_opcode_flags(opline->opcode);
uint32_t n = 0;
int len = 0;
const zend_ssa *ssa = NULL;
if (dump_flags & ZEND_DUMP_SSA) {
ssa = (const zend_ssa*)data;
}
if (!b) {
len = fprintf(stderr, "L%u:", (uint32_t)(opline - op_array->opcodes));
}
fprintf(stderr, "%*c%s", 8-len, ' ', name ? (name + 5) : "???");
fprintf(stderr, "%*c", 8-len, ' ');
if (!ssa || !ssa->ops || ssa->ops[opline - op_array->opcodes].result_use < 0) {
if (opline->result_type == IS_CV ||
opline->result_type == IS_VAR ||
opline->result_type == IS_TMP_VAR) {
if (ssa && ssa->ops) {
int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
ZEND_ASSERT(ssa_var_num >= 0);
zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
} else {
zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
}
fprintf(stderr, " = ");
} else if (!(dump_flags & ZEND_DUMP_HIDE_UNUSED_VARS) &&
(opline->result_type & IS_VAR) &&
(opline->result_type & EXT_TYPE_UNUSED)) {
fprintf(stderr, "U%u = ", EX_VAR_TO_NUM(opline->result.var));
}
}
if (name) {
fprintf(stderr, "%s", (name + 5));
} else {
fprintf(stderr, "OP_%d", (int)opline->opcode);
}
if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {
fprintf(stderr, " %u", opline->extended_value);
} else if (ZEND_VM_EXT_DIM_OBJ == (flags & ZEND_VM_EXT_MASK)) {
@ -267,13 +565,27 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
fprintf(stderr, " live-range(%u)", opline->op1.num);
}
} else if (opline->op1_type == IS_CONST) {
zend_dump_const(CT_CONSTANT_EX(op_array, opline->op1.constant));
} else if (opline->op1_type == IS_CV) {
fprintf(stderr, " CV%u", EX_VAR_TO_NUM(opline->op1.var));
} else if (opline->op1_type == IS_VAR) {
fprintf(stderr, " V%u", EX_VAR_TO_NUM(opline->op1.var));
} else if ( opline->op1_type == IS_TMP_VAR) {
fprintf(stderr, " T%u", EX_VAR_TO_NUM(opline->op1.var));
zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op1, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
} else if (opline->op1_type == IS_CV ||
opline->op1_type == IS_VAR ||
opline->op1_type == IS_TMP_VAR) {
if (ssa && ssa->ops) {
int ssa_var_num = ssa->ops[opline - op_array->opcodes].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));
}
} else {
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_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));
}
}
} else if (ZEND_VM_OP1_THIS == (flags & ZEND_VM_OP1_MASK)) {
fprintf(stderr, " THIS");
} else if (ZEND_VM_OP1_NEXT == (flags & ZEND_VM_OP1_MASK)) {
@ -298,13 +610,27 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
fprintf(stderr, " live-range(%u)", opline->op2.num);
}
} else if (opline->op2_type == IS_CONST) {
zend_dump_const(CT_CONSTANT_EX(op_array, opline->op2.constant));
} else if (opline->op2_type == IS_CV) {
fprintf(stderr, " CV%u", EX_VAR_TO_NUM(opline->op2.var));
} else if (opline->op2_type == IS_VAR) {
fprintf(stderr, " V%u", EX_VAR_TO_NUM(opline->op2.var));
} else if ( opline->op2_type == IS_TMP_VAR) {
fprintf(stderr, " T%u", EX_VAR_TO_NUM(opline->op2.var));
zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
} else if (opline->op2_type == IS_CV ||
opline->op2_type == IS_VAR ||
opline->op2_type == IS_TMP_VAR) {
if (ssa && ssa->ops) {
int ssa_var_num = ssa->ops[opline - op_array->opcodes].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));
}
} else {
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_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));
}
}
} else if (ZEND_VM_OP2_THIS == (flags & ZEND_VM_OP2_MASK)) {
fprintf(stderr, " THIS");
} else if (ZEND_VM_OP2_NEXT == (flags & ZEND_VM_OP2_MASK)) {
@ -326,22 +652,34 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
fprintf(stderr, " V%u", EX_VAR_TO_NUM(opline->extended_value));
}
if (opline->result_type == IS_CONST) {
zend_dump_const(CT_CONSTANT_EX(op_array, opline->result.constant));
} else if (opline->result_type == IS_CV) {
fprintf(stderr, " -> CV%u", EX_VAR_TO_NUM(opline->result.var));
} else if (opline->result_type & IS_VAR) {
if (opline->result_type & EXT_TYPE_UNUSED) {
fprintf(stderr, " -> U%u", EX_VAR_TO_NUM(opline->result.var));
} else {
fprintf(stderr, " -> V%u", EX_VAR_TO_NUM(opline->result.var));
zend_dump_const(CRT_CONSTANT_EX(op_array, opline->result, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
} else if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_use >= 0) {
if (opline->result_type == IS_CV ||
opline->result_type == IS_VAR ||
opline->result_type == IS_TMP_VAR) {
if (ssa && ssa->ops) {
int ssa_var_num = ssa->ops[opline - op_array->opcodes].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));
}
} else {
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_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));
}
}
}
} else if ( opline->result_type == IS_TMP_VAR) {
fprintf(stderr, " -> T%u", EX_VAR_TO_NUM(opline->result.var));
}
fprintf(stderr, "\n");
}
void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
{
zend_basic_block *b = cfg->blocks + n;
int printed = 0;
@ -380,7 +718,7 @@ void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
if (b->flags & ZEND_BB_KILL_VAR) {
fprintf(stderr, " kill_var");
}
if ((dump_flags & ZEND_DUMP_UNREACHABLE) & !(b->flags & ZEND_BB_REACHABLE)) {
if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) & !(b->flags & ZEND_BB_REACHABLE)) {
fprintf(stderr, " unreachable");
}
if (b->flags & ZEND_BB_LOOP_HEADER) {
@ -389,6 +727,7 @@ void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
if (b->flags & ZEND_BB_IRREDUCIBLE_LOOP) {
fprintf(stderr, " irreducible");
}
fprintf(stderr, " lines=[%d-%d]", b->start, b->end);
fprintf(stderr, "\n");
if (b->predecessors_count) {
@ -414,7 +753,7 @@ void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
}
if (b->idom >= 0) {
fprintf(stderr, " ; idom=%d\n", b->idom);
fprintf(stderr, " ; idom=BB%d\n", b->idom);
}
if (b->level >= 0) {
fprintf(stderr, " ; level=%d\n", b->level);
@ -434,45 +773,173 @@ void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
}
}
void zend_dump_op_array(const zend_op_array *op_array, const zend_cfg *cfg, uint32_t dump_flags, const char *msg)
static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_array, const zend_ssa *ssa, int n, uint32_t dump_flags)
{
int i;
zend_dump_block_info(cfg, n, dump_flags);
if (ssa && ssa->blocks && ssa->blocks[n].phis) {
zend_ssa_phi *p = ssa->blocks[n].phis;
do {
int j;
fprintf(stderr, " ");
zend_dump_ssa_var(op_array, ssa, p->ssa_var, 0, p->var);
if (p->pi < 0) {
fprintf(stderr, " = Phi(");
for (j = 0; j < cfg->blocks[n].predecessors_count; j++) {
if (j > 0) {
fprintf(stderr, ", ");
}
zend_dump_ssa_var(op_array, ssa, p->sources[j], 0, p->var);
}
fprintf(stderr, ")\n");
} else {
fprintf(stderr, " = Pi(");
zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var);
fprintf(stderr, " &");
zend_dump_pi_range(op_array, ssa, &p->constraint);
fprintf(stderr, ")\n");
}
p = p->next;
} while (p);
}
}
static void zend_dump_op_array_name(const zend_op_array *op_array)
{
zend_func_info *func_info = NULL;
func_info = ZEND_FUNC_INFO(op_array);
if (op_array->function_name) {
if (op_array->scope && op_array->scope->name) {
fprintf(stderr, "\n%s::%s", op_array->scope->name->val, op_array->function_name->val);
fprintf(stderr, "%s::%s", op_array->scope->name->val, op_array->function_name->val);
} else {
fprintf(stderr, "\n%s", op_array->function_name->val);
fprintf(stderr, "%s", op_array->function_name->val);
}
} else {
fprintf(stderr, "\n%s", "$_main");
fprintf(stderr, "%s", "$_main");
}
fprintf(stderr, ": ; (lines=%d, args=%d, vars=%d, tmps=%d)\n",
if (func_info && func_info->clone_num > 0) {
fprintf(stderr, "_@_clone_%d", func_info->clone_num);
}
}
void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data)
{
int i;
const zend_cfg *cfg = NULL;
const zend_ssa *ssa = NULL;
zend_func_info *func_info = NULL;
uint32_t func_flags = 0;
if (dump_flags & (ZEND_DUMP_CFG|ZEND_DUMP_SSA)) {
cfg = (const zend_cfg*)data;
}
if (dump_flags & ZEND_DUMP_SSA) {
ssa = (const zend_ssa*)data;
}
func_info = ZEND_FUNC_INFO(op_array);
if (func_info) {
func_flags = func_info->flags;
}
fprintf(stderr, "\n");
zend_dump_op_array_name(op_array);
fprintf(stderr, ": ; (lines=%d, args=%d",
op_array->last,
op_array->num_args,
op_array->last_var,
op_array->T);
op_array->num_args);
if (func_info && func_info->num_args >= 0) {
fprintf(stderr, "/%d", func_info->num_args);
}
fprintf(stderr, ", vars=%d, tmps=%d", op_array->last_var, op_array->T);
if (ssa) {
fprintf(stderr, ", ssa_vars=%d", ssa->vars_count);
}
if (func_flags & ZEND_FUNC_RECURSIVE) {
fprintf(stderr, ", recursive");
if (func_flags & ZEND_FUNC_RECURSIVE_DIRECTLY) {
fprintf(stderr, " directly");
}
if (func_flags & ZEND_FUNC_RECURSIVE_INDIRECTLY) {
fprintf(stderr, " indirectly");
}
}
if (func_flags & ZEND_FUNC_IRREDUCIBLE) {
fprintf(stderr, ", irreducable");
}
if (func_flags & ZEND_FUNC_NO_LOOPS) {
fprintf(stderr, ", no_loops");
}
//TODO: this is useful only for JIT???
#if 0
if (info->flags & ZEND_JIT_FUNC_NO_IN_MEM_CVS) {
fprintf(stderr, ", no_in_mem_cvs");
}
if (info->flags & ZEND_JIT_FUNC_NO_USED_ARGS) {
fprintf(stderr, ", no_used_args");
}
if (info->flags & ZEND_JIT_FUNC_NO_SYMTAB) {
fprintf(stderr, ", no_symtab");
}
if (info->flags & ZEND_JIT_FUNC_NO_FRAME) {
fprintf(stderr, ", no_frame");
}
if (info->flags & ZEND_JIT_FUNC_INLINE) {
fprintf(stderr, ", inline");
}
#endif
if (func_info && func_info->return_value_used == 0) {
fprintf(stderr, ", no_return_value");
} else if (func_info && func_info->return_value_used == 1) {
fprintf(stderr, ", return_value");
}
fprintf(stderr, ")\n");
if (msg) {
fprintf(stderr, " ; (%s)\n", msg);
}
fprintf(stderr, " ; %s:%u-%u\n", op_array->filename->val, op_array->line_start, op_array->line_end);
if (func_info && func_info->num_args > 0) {
for (i = 0; i < MIN(op_array->num_args, func_info->num_args ); i++) {
fprintf(stderr, " ; arg %d ", i);
zend_dump_type_info(func_info->arg_info[i].info.type, func_info->arg_info[i].info.ce, func_info->arg_info[i].info.is_instanceof);
zend_dump_range(&func_info->arg_info[i].info.range);
fprintf(stderr, "\n");
}
}
if (func_info) {
fprintf(stderr, " ; return ");
zend_dump_type_info(func_info->return_info.type, func_info->return_info.ce, func_info->return_info.is_instanceof);
zend_dump_range(&func_info->return_info.range);
fprintf(stderr, "\n");
}
if (ssa && ssa->var_info) {
for (i = 0; i < op_array->last_var; i++) {
fprintf(stderr, " ; ");
zend_dump_ssa_var(op_array, ssa, i, IS_CV, i);
fprintf(stderr, "\n");
}
}
if (cfg) {
int n;
zend_basic_block *b;
for (n = 0; n < cfg->blocks_count; n++) {
b = cfg->blocks + n;
if ((dump_flags & ZEND_DUMP_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
const zend_op *opline;
const zend_op *end;
zend_dump_block_info(cfg, n, dump_flags);
zend_dump_block_header(cfg, op_array, ssa, n, dump_flags);
if (!(b->flags & ZEND_BB_EMPTY)) {
opline = op_array->opcodes + b->start;
end = op_array->opcodes + b->end + 1;
while (opline < end) {
zend_dump_op(op_array, b, opline);
zend_dump_op(op_array, b, opline, dump_flags, data);
opline++;
}
}
@ -531,7 +998,7 @@ void zend_dump_op_array(const zend_op_array *op_array, const zend_cfg *cfg, uint
const zend_op *end = opline + op_array->last;
while (opline < end) {
zend_dump_op(op_array, NULL, opline);
zend_dump_op(op_array, NULL, opline, dump_flags, data);
opline++;
}
if (op_array->last_live_range) {
@ -585,6 +1052,130 @@ void zend_dump_op_array(const zend_op_array *op_array, const zend_cfg *cfg, uint
}
}
void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg)
{
int j;
fprintf(stderr, "\nDOMINATORS-TREE for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
for (j = 0; j < cfg->blocks_count; j++) {
zend_basic_block *b = cfg->blocks + j;
if (b->flags & ZEND_BB_REACHABLE) {
zend_dump_block_info(cfg, j, 0);
}
}
}
void zend_dump_variables(const zend_op_array *op_array)
{
int j;
fprintf(stderr, "\nCV Variables for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
for (j = 0; j < op_array->last_var; j++) {
fprintf(stderr, " ");
zend_dump_var(op_array, IS_CV, j);
fprintf(stderr, "\n");
}
}
void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa)
{
int j;
if (ssa->vars) {
fprintf(stderr, "\nSSA Variable for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
for (j = 0; j < ssa->vars_count; j++) {
fprintf(stderr, " ");
zend_dump_ssa_var(op_array, ssa, j, IS_CV, ssa->vars[j].var);
if (ssa->vars[j].scc >= 0) {
if (ssa->vars[j].scc_entry) {
fprintf(stderr, " *");
} else {
fprintf(stderr, " ");
}
fprintf(stderr, "SCC=%d", ssa->vars[j].scc);
}
fprintf(stderr, "\n");
}
}
}
static void zend_dump_var_set(const zend_op_array *op_array, const char *name, zend_bitset set)
{
int first = 1;
uint32_t i;
fprintf(stderr, " ; %s = {", name);
for (i = 0; i < op_array->last_var + op_array->T; i++) {
if (zend_bitset_in(set, i)) {
if (first) {
first = 0;
} else {
fprintf(stderr, ", ");
}
zend_dump_var(op_array, IS_CV, i);
}
}
fprintf(stderr, "}\n");
}
void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg)
{
int j;
fprintf(stderr, "\nVariable Liveness for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
for (j = 0; j < cfg->blocks_count; j++) {
fprintf(stderr, " BB%d:\n", j);
zend_dump_var_set(op_array, "gen", DFG_BITSET(dfg->gen, dfg->size, j));
zend_dump_var_set(op_array, "def", DFG_BITSET(dfg->def, dfg->size, j));
zend_dump_var_set(op_array, "use", DFG_BITSET(dfg->use, dfg->size, j));
zend_dump_var_set(op_array, "in ", DFG_BITSET(dfg->in, dfg->size, j));
zend_dump_var_set(op_array, "out", DFG_BITSET(dfg->out, dfg->size, j));
}
}
void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa)
{
int j;
zend_ssa_block *ssa_blocks = ssa->blocks;
int blocks_count = ssa->cfg.blocks_count;
fprintf(stderr, "\nSSA Phi() Placement for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
for (j = 0; j < blocks_count; j++) {
if (ssa_blocks && ssa_blocks[j].phis) {
zend_ssa_phi *p = ssa_blocks[j].phis;
int first = 1;
fprintf(stderr, " BB%d:\n", j);
if (p->pi >= 0) {
fprintf(stderr, " ; pi={");
} else {
fprintf(stderr, " ; phi={");
}
do {
if (first) {
first = 0;
} else {
fprintf(stderr, ", ");
}
zend_dump_var(op_array, IS_CV, p->var);
p = p->next;
} while (p);
fprintf(stderr, "}\n");
}
}
}
/*
* Local variables:
* tab-width: 4

View file

@ -19,12 +19,24 @@
#ifndef ZEND_DUMP_H
#define ZEND_DUMP_H
#define ZEND_DUMP_UNREACHABLE (1<<0)
#include "zend_ssa.h"
#include "zend_dfg.h"
#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0)
#define ZEND_DUMP_HIDE_UNUSED_VARS (1<<1)
#define ZEND_DUMP_CFG (1<<2)
#define ZEND_DUMP_SSA (1<<3)
#define ZEND_DUMP_RT_CONSTANTS ZEND_RT_CONSTANTS
BEGIN_EXTERN_C()
void zend_dump_op_array(const zend_op_array *op_array, const zend_cfg *cfg, uint32_t dump_flags, const char *msg);
void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags);
void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
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);
void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
END_EXTERN_C()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,70 @@
/*
+----------------------------------------------------------------------+
| Zend Engine, Func Info |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2015 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_FUNC_INFO_H
#define ZEND_FUNC_INFO_H
#include "zend_ssa.h"
/* func flags */
#define ZEND_FUNC_TOO_DYNAMIC (1<<0)
#define ZEND_FUNC_HAS_CALLS (1<<1)
#define ZEND_FUNC_VARARG (1<<2)
#define ZEND_FUNC_NO_LOOPS (1<<3)
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
#define ZEND_FUNC_RECURSIVE (1<<7)
#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
/* The following flags are valid only for return values of internal functions
* returned by zend_get_func_info()
*/
#define FUNC_MAY_WARN (1<<30)
#define FUNC_MAY_INLINE (1<<31)
typedef struct _zend_func_info zend_func_info;
typedef struct _zend_call_info zend_call_info;
#define ZEND_FUNC_INFO(op_array) \
((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
#define ZEND_SET_FUNC_INFO(op_array, info) do { \
zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
*pinfo = info; \
} while (0)
BEGIN_EXTERN_C()
extern int zend_func_info_rid;
uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa);
int zend_func_info_startup(void);
int zend_func_info_shutdown(void);
END_EXTERN_C()
#endif /* ZEND_FUNC_INFO_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,292 @@
/*
+----------------------------------------------------------------------+
| Zend Engine, e-SSA based Type & Range Inference |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2015 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_INFERENCE_H
#define ZEND_INFERENCE_H
#include "zend_optimizer.h"
#include "zend_ssa.h"
#include "zend_bitset.h"
/* Bitmask for type inference (zend_ssa_var_info.type) */
#define MAY_BE_UNDEF (1 << IS_UNDEF)
#define MAY_BE_NULL (1 << IS_NULL)
#define MAY_BE_FALSE (1 << IS_FALSE)
#define MAY_BE_TRUE (1 << IS_TRUE)
#define MAY_BE_LONG (1 << IS_LONG)
#define MAY_BE_DOUBLE (1 << IS_DOUBLE)
#define MAY_BE_STRING (1 << IS_STRING)
#define MAY_BE_ARRAY (1 << IS_ARRAY)
#define MAY_BE_OBJECT (1 << IS_OBJECT)
#define MAY_BE_RESOURCE (1 << IS_RESOURCE)
#define MAY_BE_ANY (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)
#define MAY_BE_REF (1 << IS_REFERENCE) /* may be reference */
#define MAY_BE_ARRAY_SHIFT (IS_REFERENCE)
#define MAY_BE_ARRAY_OF_NULL (MAY_BE_NULL << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_FALSE (MAY_BE_FALSE << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_TRUE (MAY_BE_TRUE << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_LONG (MAY_BE_LONG << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_DOUBLE (MAY_BE_DOUBLE << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_STRING (MAY_BE_STRING << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_ARRAY (MAY_BE_ARRAY << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_OBJECT (MAY_BE_OBJECT << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_RESOURCE (MAY_BE_RESOURCE << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_ANY (MAY_BE_ANY << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_REF (MAY_BE_REF << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_KEY_LONG (1<<21)
#define MAY_BE_ARRAY_KEY_STRING (1<<22)
#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING)
#define MAY_BE_ERROR (1<<23)
#define MAY_BE_CLASS (1<<24)
#define MAY_BE_IN_REG (1<<25) /* value allocated in CPU register */
//TODO: remome MAY_BE_DEF, MAY_BE_RC1, MAY_BE_RCN???
#define MAY_BE_DEF (1<<26)
#define MAY_BE_RC1 (1<<27) /* may be non-reference with refcount == 1 */
#define MAY_BE_RCN (1<<28) /* may be non-reference with refcount > 1 */
#define DEFINE_SSA_OP_HAS_RANGE(opN) \
static zend_always_inline long _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
{ \
if (opline->opN##_type == IS_CONST) { \
zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
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); \
} \
return 0; \
}
#define DEFINE_SSA_OP_MIN_RANGE(opN) \
static zend_always_inline long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
{ \
if (opline->opN##_type == IS_CONST) { \
zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
if (Z_TYPE_P(zv) == IS_LONG) { \
return Z_LVAL_P(zv); \
} else if (Z_TYPE_P(zv) == IS_TRUE) { \
return 1; \
} else if (Z_TYPE_P(zv) == IS_FALSE) { \
return 0; \
} else if (Z_TYPE_P(zv) == IS_NULL) { \
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; \
} \
return LONG_MIN; \
}
#define DEFINE_SSA_OP_MAX_RANGE(opN) \
static zend_always_inline long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
{ \
if (opline->opN##_type == IS_CONST) { \
zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
if (Z_TYPE_P(zv) == IS_LONG) { \
return Z_LVAL_P(zv); \
} else if (Z_TYPE_P(zv) == IS_TRUE) { \
return 1; \
} else if (Z_TYPE_P(zv) == IS_FALSE) { \
return 0; \
} else if (Z_TYPE_P(zv) == IS_NULL) { \
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; \
} \
return LONG_MAX; \
}
#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) \
{ \
if (opline->opN##_type == IS_CONST) { \
zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
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; \
} \
return 1; \
}
#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) \
{ \
if (opline->opN##_type == IS_CONST) { \
zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
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; \
} \
return 1; \
}
DEFINE_SSA_OP_HAS_RANGE(op1)
DEFINE_SSA_OP_MIN_RANGE(op1)
DEFINE_SSA_OP_MAX_RANGE(op1)
DEFINE_SSA_OP_RANGE_UNDERFLOW(op1)
DEFINE_SSA_OP_RANGE_OVERFLOW(op1)
DEFINE_SSA_OP_HAS_RANGE(op2)
DEFINE_SSA_OP_MIN_RANGE(op2)
DEFINE_SSA_OP_MAX_RANGE(op2)
DEFINE_SSA_OP_RANGE_UNDERFLOW(op2)
DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
#define OP1_HAS_RANGE() (_ssa_op1_has_range (op_array, ssa, opline))
#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline))
#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline))
#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline))
#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline))
#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline))
#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline))
#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline))
#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline))
#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline))
static zend_always_inline uint32_t _const_op_type(const zval *zv) {
if (Z_TYPE_P(zv) == IS_CONSTANT) {
return MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
} else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
return MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
} else if (Z_TYPE_P(zv) == IS_ARRAY) {
HashTable *ht = Z_ARRVAL_P(zv);
uint32_t tmp = MAY_BE_ARRAY | MAY_BE_DEF | MAY_BE_RC1;
zend_string *str;
zval *val;
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
if (str) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
} else {
tmp |= MAY_BE_ARRAY_KEY_LONG;
}
tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
} ZEND_HASH_FOREACH_END();
return tmp;
} else {
return (1 << Z_TYPE_P(zv)) | MAY_BE_DEF | MAY_BE_RC1;
}
}
static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
{
if (ssa->var_info && ssa_var_num >= 0) {
return ssa->var_info[ssa_var_num].type;
} else {
return MAY_BE_DEF | MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ERROR;
}
}
#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) \
{ \
if (opline->opN##_type == IS_CONST) { \
return _const_op_type(CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants)); \
} else { \
return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_use : -1); \
} \
}
#define DEFINE_SSA_OP_DEF_INFO(opN) \
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); \
}
DEFINE_SSA_OP_INFO(op1)
DEFINE_SSA_OP_INFO(op2)
DEFINE_SSA_OP_INFO(result)
DEFINE_SSA_OP_DEF_INFO(op1)
DEFINE_SSA_OP_DEF_INFO(op2)
DEFINE_SSA_OP_DEF_INFO(result)
#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline))
#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline))
#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1)))
#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1)))
#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline))
#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline))
#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline))
#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1)))
#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))
BEGIN_EXTERN_C()
int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa);
uint32_t zend_array_element_type(uint32_t t1, int write, int insert);
int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp);
void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_bool underflow, long min, long max, zend_bool overflow);
int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
void zend_inference_check_recursive_dependencies(zend_op_array *op_array);
int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist);
void zend_func_return_info(const zend_op_array *op_array,
int recursive,
int widening,
zend_ssa_var_info *ret);
END_EXTERN_C()
#endif /* ZEND_INFERENCE_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/

View file

@ -27,6 +27,7 @@
#include "zend_execute.h"
#include "zend_vm.h"
#include "zend_cfg.h"
#include "zend_func_info.h"
#include "zend_dump.h"
static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
@ -549,7 +550,7 @@ static void zend_optimize(zend_op_array *op_array,
}
if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
zend_dump_op_array(op_array, NULL, 1, "before optimizer");
zend_dump_op_array(op_array, 0, "before optimizer", NULL);
}
/* pass 1
@ -561,7 +562,7 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
zend_optimizer_pass1(op_array, ctx);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
zend_dump_op_array(op_array, NULL, 1, "after pass 1");
zend_dump_op_array(op_array, 0, "after pass 1", NULL);
}
}
@ -574,7 +575,7 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
zend_optimizer_pass2(op_array);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
zend_dump_op_array(op_array, NULL, 1, "after pass 2");
zend_dump_op_array(op_array, 0, "after pass 2", NULL);
}
}
@ -586,7 +587,7 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
zend_optimizer_pass3(op_array);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
zend_dump_op_array(op_array, NULL, 1, "after pass 1");
zend_dump_op_array(op_array, 0, "after pass 1", NULL);
}
}
@ -596,7 +597,7 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
optimize_func_calls(op_array, ctx);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
zend_dump_op_array(op_array, NULL, 1, "after pass 1");
zend_dump_op_array(op_array, 0, "after pass 1", NULL);
}
}
@ -606,7 +607,7 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
optimize_cfg(op_array, ctx);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
zend_dump_op_array(op_array, NULL, 1, "after pass 5");
zend_dump_op_array(op_array, 0, "after pass 5", NULL);
}
}
@ -616,7 +617,7 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) {
optimize_dfa(op_array, ctx);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
zend_dump_op_array(op_array, NULL, 1, "after pass 6");
zend_dump_op_array(op_array, 0, "after pass 6", NULL);
}
}
@ -626,7 +627,7 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
optimize_temporary_variables(op_array, ctx);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
zend_dump_op_array(op_array, NULL, 1, "after pass 9");
zend_dump_op_array(op_array, 0, "after pass 9", NULL);
}
}
@ -636,7 +637,7 @@ static void zend_optimize(zend_op_array *op_array,
if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
zend_optimizer_nop_removal(op_array);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
zend_dump_op_array(op_array, NULL, 1, "after pass 10");
zend_dump_op_array(op_array, 0, "after pass 10", NULL);
}
}
@ -646,12 +647,12 @@ static void zend_optimize(zend_op_array *op_array,
if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
zend_optimizer_compact_literals(op_array, ctx);
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
zend_dump_op_array(op_array, NULL, 1, "after pass 11");
zend_dump_op_array(op_array, 0, "after pass 11", NULL);
}
}
if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
zend_dump_op_array(op_array, NULL, 1, "after optimizer");
zend_dump_op_array(op_array, 0, "after optimizer", NULL);
}
}
@ -794,3 +795,21 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend
return 1;
}
int zend_optimizer_startup(void)
{
return zend_func_info_startup();
}
int zend_optimizer_shutdown(void)
{
return zend_func_info_shutdown();
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/

View file

@ -71,6 +71,10 @@
#define ZEND_DUMP_AFTER_DFA_PASS (1<<22)
#define ZEND_DUMP_DFA_CFG (1<<23)
#define ZEND_DUMP_DFA_DOMINATORS (1<<24)
#define ZEND_DUMP_DFA_LIVENESS (1<<25)
#define ZEND_DUMP_DFA_PHI (1<<26)
#define ZEND_DUMP_DFA_SSA (1<<27)
#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
typedef struct _zend_script {
zend_string *filename;
@ -80,5 +84,7 @@ typedef struct _zend_script {
} zend_script;
int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level);
int zend_optimizer_startup(void);
int zend_optimizer_shutdown(void);
#endif

View file

@ -20,10 +20,11 @@
#include "zend_compile.h"
#include "zend_dfg.h"
#include "zend_ssa.h"
#include "zend_dump.h"
static int needs_pi(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */
static int needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */
{
if (from == to || cfg->blocks[to].predecessors_count != 1) {
if (from == to || ssa->cfg.blocks[to].predecessors_count != 1) {
zend_ssa_phi *p = ssa->blocks[to].phis;
while (p) {
if (p->pi < 0 && p->var == var) {
@ -37,20 +38,20 @@ static int needs_pi(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg, zend_
}
/* }}} */
static int add_pi(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var, int min_var, int max_var, long min, long max, char underflow, char overflow, char negative) /* {{{ */
static int add_pi(zend_arena **arena, const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var, int min_var, int max_var, long min, long max, char underflow, char overflow, char negative) /* {{{ */
{
if (needs_pi(op_array, cfg, dfg, ssa, from, to, var)) {
if (needs_pi(op_array, dfg, ssa, from, to, var)) {
zend_ssa_phi *phi = zend_arena_calloc(arena, 1,
sizeof(zend_ssa_phi) +
sizeof(int) * cfg->blocks[to].predecessors_count +
sizeof(void*) * cfg->blocks[to].predecessors_count);
sizeof(int) * ssa->cfg.blocks[to].predecessors_count +
sizeof(void*) * ssa->cfg.blocks[to].predecessors_count);
if (!phi) {
return FAILURE;
}
phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi));
memset(phi->sources, 0xff, sizeof(int) * cfg->blocks[to].predecessors_count);
phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * cfg->blocks[to].predecessors_count);
memset(phi->sources, 0xff, sizeof(int) * ssa->cfg.blocks[to].predecessors_count);
phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[to].predecessors_count);
phi->pi = from;
phi->constraint.min_var = min_var;
@ -71,9 +72,9 @@ static int add_pi(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, ze
}
/* }}} */
static int zend_ssa_rename(zend_op_array *op_array, zend_cfg *cfg, zend_ssa *ssa, int *var, int n) /* {{{ */
static int zend_ssa_rename(const zend_op_array *op_array, zend_ssa *ssa, int *var, int n) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
zend_basic_block *blocks = ssa->cfg.blocks;
zend_ssa_block *ssa_blocks = ssa->blocks;
zend_ssa_op *ssa_ops = ssa->ops;
int ssa_vars_count = ssa->vars_count;
@ -327,7 +328,7 @@ static int zend_ssa_rename(zend_op_array *op_array, zend_cfg *cfg, zend_ssa *ssa
} else if (p->pi < 0) {
/* Normal Phi */
for (j = 0; j < blocks[succ].predecessors_count; j++)
if (cfg->predecessors[blocks[succ].predecessor_offset + j] == n) {
if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
break;
}
ZEND_ASSERT(j < blocks[succ].predecessors_count);
@ -340,7 +341,7 @@ static int zend_ssa_rename(zend_op_array *op_array, zend_cfg *cfg, zend_ssa *ssa
while (q) {
if (q->pi < 0 && q->var == p->var) {
for (j = 0; j < blocks[succ].predecessors_count; j++) {
if (cfg->predecessors[blocks[succ].predecessor_offset + j] == n) {
if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
break;
}
}
@ -359,7 +360,7 @@ static int zend_ssa_rename(zend_op_array *op_array, zend_cfg *cfg, zend_ssa *ssa
j = blocks[n].children;
while (j >= 0) {
// FIXME: Tail call optimization?
if (zend_ssa_rename(op_array, cfg, ssa, var, j) != SUCCESS)
if (zend_ssa_rename(op_array, ssa, var, j) != SUCCESS)
return FAILURE;
j = blocks[j].next_child;
}
@ -368,17 +369,18 @@ static int zend_ssa_rename(zend_op_array *op_array, zend_cfg *cfg, zend_ssa *ssa
}
/* }}} */
int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, int rt_constants, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */
int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
zend_basic_block *blocks = ssa->cfg.blocks;
zend_ssa_block *ssa_blocks;
int blocks_count = cfg->blocks_count;
int blocks_count = ssa->cfg.blocks_count;
uint32_t set_size;
zend_bitset tmp, gen, in;
int *var = 0;
int *var = NULL;
int i, j, k, changed;
zend_dfg dfg;
ssa->rt_constants = (build_flags & ZEND_RT_CONSTANTS);
ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block));
if (!ssa_blocks) {
return FAILURE;
@ -396,10 +398,14 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
dfg.in = dfg.use + set_size * blocks_count;
dfg.out = dfg.in + set_size * blocks_count;
if (zend_build_dfg(op_array, cfg, &dfg) != SUCCESS) {
if (zend_build_dfg(op_array, &ssa->cfg, &dfg) != SUCCESS) {
return FAILURE;
}
if (build_flags & ZEND_SSA_DEBUG_LIVENESS) {
zend_dump_dfg(op_array, &ssa->cfg, &dfg);
}
tmp = dfg.tmp;
gen = dfg.gen;
in = dfg.in;
@ -414,7 +420,7 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
if (j >= 0 && (blocks[j].predecessors_count > 1 || j == 0)) {
zend_bitset_copy(tmp, gen + (j * set_size), set_size);
for (k = 0; k < blocks[j].predecessors_count; k++) {
i = cfg->predecessors[blocks[j].predecessor_offset + k];
i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
while (i != blocks[j].idom) {
zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size);
i = blocks[i].idom;
@ -448,7 +454,7 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
zend_bitset_copy(tmp, in + (j * set_size), set_size);
} else {
for (k = 0; k < blocks[j].predecessors_count; k++) {
i = cfg->predecessors[blocks[j].predecessor_offset + k];
i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
while (i != blocks[j].idom) {
zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size);
i = blocks[i].idom;
@ -470,7 +476,7 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
return FAILURE;
phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi));
memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * cfg->blocks[j].predecessors_count);
phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count);
phi->pi = -1;
phi->var = i;
@ -488,7 +494,7 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
* Order of Phis is importent, Pis must be placed before Phis
*/
for (j = 0; j < blocks_count; j++) {
zend_op *opline = op_array->opcodes + cfg->blocks[j].end;
zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].end;
int bt; /* successor block number if a condition is true */
int bf; /* successor block number if a condition is false */
@ -500,30 +506,30 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
*/
switch (opline->opcode) {
case ZEND_JMPZ:
if (cfg->blocks[cfg->blocks[j].successors[0]].start == OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes) {
bf = cfg->blocks[j].successors[0];
bt = cfg->blocks[j].successors[1];
if (ssa->cfg.blocks[ssa->cfg.blocks[j].successors[0]].start == OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes) {
bf = ssa->cfg.blocks[j].successors[0];
bt = ssa->cfg.blocks[j].successors[1];
} else {
bt = cfg->blocks[j].successors[0];
bf = cfg->blocks[j].successors[1];
bt = ssa->cfg.blocks[j].successors[0];
bf = ssa->cfg.blocks[j].successors[1];
}
break;
case ZEND_JMPNZ:
if (cfg->blocks[cfg->blocks[j].successors[0]].start == OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes) {
bt = cfg->blocks[j].successors[0];
bf = cfg->blocks[j].successors[1];
if (ssa->cfg.blocks[ssa->cfg.blocks[j].successors[0]].start == OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes) {
bt = ssa->cfg.blocks[j].successors[0];
bf = ssa->cfg.blocks[j].successors[1];
} else {
bf = cfg->blocks[j].successors[0];
bt = cfg->blocks[j].successors[1];
bf = ssa->cfg.blocks[j].successors[0];
bt = ssa->cfg.blocks[j].successors[1];
}
break;
case ZEND_JMPZNZ:
if (cfg->blocks[cfg->blocks[j].successors[0]].start == OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes) {
bf = cfg->blocks[j].successors[0];
bt = cfg->blocks[j].successors[1];
if (ssa->cfg.blocks[ssa->cfg.blocks[j].successors[0]].start == OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes) {
bf = ssa->cfg.blocks[j].successors[0];
bt = ssa->cfg.blocks[j].successors[1];
} else {
bt = cfg->blocks[j].successors[0];
bf = cfg->blocks[j].successors[1];
bt = ssa->cfg.blocks[j].successors[0];
bf = ssa->cfg.blocks[j].successors[1];
}
break;
default:
@ -661,34 +667,34 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
if (var1 >= 0) {
if ((opline-1)->opcode == ZEND_IS_EQUAL) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var1, var2, var2, val2, val2, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var1, var2, var2, val2, val2, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var1, var2, var2, val2, val2, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var1, var2, var2, val2, val2, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var1, var2, var2, val2, val2, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var1, var2, var2, val2, val2, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var1, var2, var2, val2, val2, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var1, var2, var2, val2, val2, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
if (val2 > LONG_MIN) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var1, -1, var2, LONG_MIN, val2-1, 1, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var1, -1, var2, LONG_MIN, val2-1, 1, 0, 0) != SUCCESS) {
return FAILURE;
}
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var1, var2, -1, val2, LONG_MAX, 0, 1, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var1, var2, -1, val2, LONG_MAX, 0, 1, 0) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var1, -1, var2, LONG_MIN, val2, 1, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var1, -1, var2, LONG_MIN, val2, 1, 0, 0) != SUCCESS) {
return FAILURE;
}
if (val2 < LONG_MAX) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var1, var2, -1, val2+1, LONG_MAX, 0, 1, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var1, var2, -1, val2+1, LONG_MAX, 0, 1, 0) != SUCCESS) {
return FAILURE;
}
}
@ -696,35 +702,34 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
}
if (var2 >= 0) {
if((opline-1)->opcode == ZEND_IS_EQUAL) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var2, var1, var1, val1, val1, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var2, var1, var1, val1, val1, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var2, var1, var1, val1, val1, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var2, var1, var1, val1, val1, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var2, var1, var1, val1, val1, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var2, var1, var1, val1, val1, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var2, var1, var1, val1, val1, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var2, var1, var1, val1, val1, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
if (val1 < LONG_MAX) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var2, var1, -1, val1+1, LONG_MAX, 0, 1, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var2, var1, -1, val1+1, LONG_MAX, 0, 1, 0) != SUCCESS) {
return FAILURE;
}
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var2, -1, var1, LONG_MIN, val1, 1, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var2, -1, var1, LONG_MIN, val1, 1, 0, 0) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var2, var1, -1, val1, LONG_MAX, 0 ,1, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var2, var1, -1, val1, LONG_MAX, 0 ,1, 0) != SUCCESS) {
return FAILURE;
}
if (val1 > LONG_MIN) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var2, -1, var1, LONG_MIN, val1-1, 1, 0, 0) != SUCCESS) {
return FAILURE;
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var2, -1, var1, LONG_MIN, val1-1, 1, 0, 0) != SUCCESS) { return FAILURE;
}
}
}
@ -737,17 +742,17 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
int var = EX_VAR_TO_NUM((opline-1)->op1.var);
if ((opline-1)->opcode == ZEND_POST_DEC) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var, -1, -1, -1, -1, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var, -1, -1, -1, -1, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var, -1, -1, -1, -1, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var, -1, -1, -1, -1, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_POST_INC) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var, -1, -1, 1, 1, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var, -1, -1, 1, 1, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var, -1, -1, 1, 1, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var, -1, -1, 1, 1, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
}
@ -759,19 +764,19 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
int var = EX_VAR_TO_NUM((opline-1)->op1.var);
if ((opline-1)->opcode == ZEND_PRE_DEC) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var, -1, -1, 0, 0, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var, -1, -1, 0, 0, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
/* speculative */
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var, -1, -1, 0, 0, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var, -1, -1, 0, 0, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
} else if ((opline-1)->opcode == ZEND_PRE_INC) {
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bf, var, -1, -1, 0, 0, 0, 0, 0) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bf, var, -1, -1, 0, 0, 0, 0, 0) != SUCCESS) {
return FAILURE;
}
/* speculative */
if (add_pi(arena, op_array, cfg, &dfg, ssa, j, bt, var, -1, -1, 0, 0, 0, 0, 1) != SUCCESS) {
if (add_pi(arena, op_array, &dfg, ssa, j, bt, var, -1, -1, 0, 0, 0, 0, 1) != SUCCESS) {
return FAILURE;
}
}
@ -792,7 +797,7 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
zend_bitset_copy(tmp, in + (j * set_size), set_size);
} else {
for (k = 0; k < blocks[j].predecessors_count; k++) {
i = cfg->predecessors[blocks[j].predecessor_offset + k];
i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
while (i != blocks[j].idom) {
zend_ssa_phi *p = ssa_blocks[i].phis;
while (p) {
@ -835,7 +840,7 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
return FAILURE;
phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi));
memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * cfg->blocks[j].predecessors_count);
phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count);
phi->pi = -1;
phi->var = i;
@ -849,9 +854,9 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
}
}
//???D if (ZCG(accel_directives).jit_debug & JIT_DEBUG_DUMP_PHI) {
//???D zend_jit_dump(op_array, JIT_DUMP_PHI_PLACEMENT);
//???D }
if (build_flags & ZEND_SSA_DEBUG_PHI_PLACEMENT) {
zend_dump_phi_placement(op_array, ssa);
}
/* SSA construction, Step 3: Renaming */
ssa->ops = zend_arena_calloc(arena, op_array->last, sizeof(zend_ssa_op));
@ -862,7 +867,7 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
var[j] = j;
}
ssa->vars_count = op_array->last_var;
if (zend_ssa_rename(op_array, cfg, ssa, var, 0) != SUCCESS) {
if (zend_ssa_rename(op_array, ssa, var, 0) != SUCCESS) {
return FAILURE;
}
@ -870,6 +875,107 @@ int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, i
}
/* }}} */
int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
{
zend_ssa_var *ssa_vars;
int i;
if (!ssa->vars) {
ssa->vars = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var));
}
ssa_vars = ssa->vars;
for (i = 0; i < op_array->last_var; i++) {
ssa_vars[i].var = i;
ssa_vars[i].scc = -1;
ssa_vars[i].definition = -1;
ssa_vars[i].use_chain = -1;
}
for (i = op_array->last_var; i < ssa->vars_count; i++) {
ssa_vars[i].var = -1;
ssa_vars[i].scc = -1;
ssa_vars[i].definition = -1;
ssa_vars[i].use_chain = -1;
}
for (i = op_array->last - 1; i >= 0; i--) {
zend_ssa_op *op = ssa->ops + i;
if (op->op1_use >= 0) {
op->op1_use_chain = ssa_vars[op->op1_use].use_chain;
ssa_vars[op->op1_use].use_chain = i;
}
if (op->op2_use >= 0 && op->op2_use != op->op1_use) {
op->op2_use_chain = ssa_vars[op->op2_use].use_chain;
ssa_vars[op->op2_use].use_chain = i;
}
if (op->result_use >= 0) {
op->res_use_chain = ssa_vars[op->result_use].use_chain;
ssa_vars[op->result_use].use_chain = i;
}
if (op->op1_def >= 0) {
ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var);
ssa_vars[op->op1_def].definition = i;
}
if (op->op2_def >= 0) {
ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var);
ssa_vars[op->op2_def].definition = i;
}
if (op->result_def >= 0) {
ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var);
ssa_vars[op->result_def].definition = i;
}
}
for (i = 0; i < ssa->cfg.blocks_count; i++) {
zend_ssa_phi *phi = ssa->blocks[i].phis;
while (phi) {
phi->block = i;
ssa_vars[phi->ssa_var].var = phi->var;
ssa_vars[phi->ssa_var].definition_phi = phi;
if (phi->pi >= 0) {
if (phi->sources[0] >= 0) {
zend_ssa_phi *p = ssa_vars[phi->sources[0]].phi_use_chain;
while (p && p != phi) {
p = zend_ssa_next_use_phi(ssa, phi->sources[0], p);
}
if (!p) {
phi->use_chains[0] = ssa_vars[phi->sources[0]].phi_use_chain;
ssa_vars[phi->sources[0]].phi_use_chain = phi;
}
}
/* min and max variables can't be used together */
if (phi->constraint.min_ssa_var >= 0) {
phi->sym_use_chain = ssa_vars[phi->constraint.min_ssa_var].sym_use_chain;
ssa_vars[phi->constraint.min_ssa_var].sym_use_chain = phi;
} else if (phi->constraint.max_ssa_var >= 0) {
phi->sym_use_chain = ssa_vars[phi->constraint.max_ssa_var].sym_use_chain;
ssa_vars[phi->constraint.max_ssa_var].sym_use_chain = phi;
}
} else {
int j;
for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
if (phi->sources[j] >= 0) {
zend_ssa_phi *p = ssa_vars[phi->sources[j]].phi_use_chain;
while (p && p != phi) {
p = zend_ssa_next_use_phi(ssa, phi->sources[j], p);
}
if (!p) {
phi->use_chains[j] = ssa_vars[phi->sources[j]].phi_use_chain;
ssa_vars[phi->sources[j]].phi_use_chain = phi;
}
}
}
}
phi = phi->next;
}
}
return SUCCESS;
}
/* }}} */
/*
* Local variables:
* tab-width: 4

View file

@ -92,19 +92,58 @@ typedef struct _zend_ssa_var {
unsigned int scc_entry : 1;
} zend_ssa_var;
typedef struct _zend_ssa_var_info {
uint32_t type; /* inferred type (see zend_inference.h) */
zend_ssa_range range;
zend_class_entry *ce;
unsigned int has_range : 1;
unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
unsigned int recursive : 1;
unsigned int use_as_double : 1;
} zend_ssa_var_info;
typedef struct _zend_ssa {
zend_cfg cfg; /* control flow graph */
int rt_constants; /* run-time or compile-time */
int vars_count; /* number of SSA variables */
zend_ssa_block *blocks; /* array of SSA blocks */
zend_ssa_op *ops; /* array of SSA instructions */
zend_ssa_var *vars; /* use/def chain of SSA variables */
int sccs; /* number of SCCs */
zend_ssa_var_info *var_info;
} zend_ssa;
BEGIN_EXTERN_C()
int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, int rt_constants, zend_ssa *ssa, uint32_t *func_flags);
int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags);
int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
END_EXTERN_C()
static zend_always_inline int zend_ssa_next_use(zend_ssa_op *ssa_op, int var, int use)
{
ssa_op += use;
if (ssa_op->result_use == var) {
return ssa_op->res_use_chain;
}
return (ssa_op->op1_use == var) ? ssa_op->op1_use_chain : ssa_op->op2_use_chain;
}
static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(zend_ssa *ssa, int var, zend_ssa_phi *p)
{
if (p->pi >= 0) {
return p->use_chains[0];
} else {
int j;
for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
if (p->sources[j] == var) {
return p->use_chains[j];
}
}
}
return NULL;
}
#endif /* ZEND_SSA_H */
/*

View file

@ -1,8 +1,8 @@
/*
+----------------------------------------------------------------------+
| Zend OPcache JIT |
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2014 The PHP Group |
| Copyright (c) 1998-2015 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |

View file

@ -407,6 +407,9 @@ fi
Optimizer/zend_dfg.c \
Optimizer/dfa_pass.c \
Optimizer/zend_ssa.c \
Optimizer/zend_inference.c \
Optimizer/zend_func_info.c \
Optimizer/zend_call_graph.c \
Optimizer/zend_dump.c,
shared,,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1,,yes)

View file

@ -23,7 +23,7 @@ if (PHP_OPCACHE != "no") {
zend_shared_alloc.c \
shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_dump.c", "opcache", "OptimizerObj");
ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c", "opcache", "OptimizerObj");
ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname);