mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Added e-SSA based DFA optimisation framework (incomplete)
This commit is contained in:
parent
8d6d28d9c6
commit
c88ffa9a56
21 changed files with 7085 additions and 188 deletions
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
304
ext/opcache/Optimizer/zend_call_graph.c
Normal file
304
ext/opcache/Optimizer/zend_call_graph.c
Normal 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:
|
||||
*/
|
83
ext/opcache/Optimizer/zend_call_graph.h
Normal file
83
ext/opcache/Optimizer/zend_call_graph.h
Normal 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:
|
||||
*/
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
/* }}} */
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
1288
ext/opcache/Optimizer/zend_func_info.c
Normal file
1288
ext/opcache/Optimizer/zend_func_info.c
Normal file
File diff suppressed because it is too large
Load diff
70
ext/opcache/Optimizer/zend_func_info.h
Normal file
70
ext/opcache/Optimizer/zend_func_info.h
Normal 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:
|
||||
*/
|
4060
ext/opcache/Optimizer/zend_inference.c
Normal file
4060
ext/opcache/Optimizer/zend_inference.c
Normal file
File diff suppressed because it is too large
Load diff
292
ext/opcache/Optimizer/zend_inference.h
Normal file
292
ext/opcache/Optimizer/zend_inference.h
Normal 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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue