]> granicus.if.org Git - php/commitdiff
Added e-SSA based DFA optimisation framework (incomplete)
authorDmitry Stogov <dmitry@zend.com>
Tue, 15 Dec 2015 21:49:44 +0000 (00:49 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 15 Dec 2015 21:49:44 +0000 (00:49 +0300)
21 files changed:
ext/opcache/Optimizer/block_pass.c
ext/opcache/Optimizer/dfa_pass.c
ext/opcache/Optimizer/zend_call_graph.c [new file with mode: 0644]
ext/opcache/Optimizer/zend_call_graph.h [new file with mode: 0644]
ext/opcache/Optimizer/zend_cfg.c
ext/opcache/Optimizer/zend_cfg.h
ext/opcache/Optimizer/zend_dfg.c
ext/opcache/Optimizer/zend_dfg.h
ext/opcache/Optimizer/zend_dump.c
ext/opcache/Optimizer/zend_dump.h
ext/opcache/Optimizer/zend_func_info.c [new file with mode: 0644]
ext/opcache/Optimizer/zend_func_info.h [new file with mode: 0644]
ext/opcache/Optimizer/zend_inference.c [new file with mode: 0644]
ext/opcache/Optimizer/zend_inference.h [new file with mode: 0644]
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/Optimizer/zend_optimizer.h
ext/opcache/Optimizer/zend_ssa.c
ext/opcache/Optimizer/zend_ssa.h
ext/opcache/Optimizer/zend_worklist.h
ext/opcache/config.m4
ext/opcache/config.w32

index c288e00bff177c29e1a5e30186a00b98cc53443b..dc49ef216d301b59f5eab2876068c6a9ae87403f 100644 (file)
@@ -1745,13 +1745,13 @@ void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
 
     /* Build CFG */
        checkpoint = zend_arena_checkpoint(ctx->arena);
-       if (zend_build_cfg(&ctx->arena, op_array, 0, 0, &cfg, NULL) != SUCCESS) {
+       if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg, NULL) != SUCCESS) {
                zend_arena_release(&ctx->arena, checkpoint);
                return;
        }
 
        if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
-               zend_dump_op_array(op_array, &cfg, ZEND_DUMP_UNREACHABLE, "before block pass");
+               zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
        }
 
        if (op_array->last_var || op_array->T) {
@@ -1805,7 +1805,7 @@ void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
        assemble_code_blocks(&cfg, op_array);
 
        if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
-               zend_dump_op_array(op_array, &cfg, 0, "after block pass");
+               zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
        }
 
        /* Destroy CFG */
index b0d4d236f79b7af090c4775e77a7b8abfea6733d..f4ec641c40f3ccde9419ad5b6aa5f1e8e5c4b713 100644 (file)
@@ -26,6 +26,8 @@
 #include "zend_bitset.h"
 #include "zend_cfg.h"
 #include "zend_ssa.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
 #include "zend_dump.h"
 
 #ifndef HAVE_DFA_PASS
@@ -35,8 +37,8 @@
 void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
 {
        void *checkpoint;
-       uint32_t flags;
-       zend_cfg cfg;
+       uint32_t build_flags;
+       uint32_t flags = 0;
        zend_ssa ssa;
 
 #if !HAVE_DFA_PASS
@@ -44,9 +46,10 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
 #endif
 
     /* Build SSA */
+       memset(&ssa, 0, sizeof(ssa));
        checkpoint = zend_arena_checkpoint(ctx->arena);
 
-       if (zend_build_cfg(&ctx->arena, op_array, 0, 0, &cfg, &flags) != SUCCESS) {
+       if (zend_build_cfg(&ctx->arena, op_array, 0, &ssa.cfg, &flags) != SUCCESS) {
                zend_arena_release(&ctx->arena, checkpoint);
                return;
        }
@@ -56,52 +59,79 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
                return;
        }
 
-       if (zend_cfg_build_predecessors(&ctx->arena, &cfg) != SUCCESS) {
+       if (zend_cfg_build_predecessors(&ctx->arena, &ssa.cfg) != SUCCESS) {
                zend_arena_release(&ctx->arena, checkpoint);
                return;
        }
 
        if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
-               zend_dump_op_array(op_array, &cfg, 0, "dfa cfg");
+               zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNUSED_VARS, "dfa cfg", &ssa.cfg);
        }
 
        /* Compute Dominators Tree */
-       if (zend_cfg_compute_dominators_tree(op_array, &cfg) != SUCCESS) {
+       if (zend_cfg_compute_dominators_tree(op_array, &ssa.cfg) != SUCCESS) {
                zend_arena_release(&ctx->arena, checkpoint);
                return;
        }
 
        /* Identify reducible and irreducible loops */
-       if (zend_cfg_identify_loops(op_array, &cfg, &flags) != SUCCESS) {
+       if (zend_cfg_identify_loops(op_array, &ssa.cfg, &flags) != SUCCESS) {
                zend_arena_release(&ctx->arena, checkpoint);
                return;
        }
 
        if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
-               int j;
+               zend_dump_dominators(op_array, &ssa.cfg);
+       }
 
-               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);
-                       }
-               }
+       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 (zend_build_ssa(&ctx->arena, op_array, &cfg, 0, &ssa, &flags) != SUCCESS) {
+       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);
+       }
+
+
+       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, &cfg, ZEND_DUMP_UNREACHABLE, "before dfa pass");
+               zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "before dfa pass", &ssa);
        }
 
-       //TODO: ???
+       //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 */
diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c
new file mode 100644 (file)
index 0000000..86db613
--- /dev/null
@@ -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:
+ */
diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h
new file mode 100644 (file)
index 0000000..2b19d69
--- /dev/null
@@ -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:
+ */
index 8fc31ec77bc187cd9c76b8fd8c29b641d578b75e..7818836d44b1231ac2c3bf6d8a1ad9c934066fa3 100644 (file)
@@ -19,6 +19,7 @@
 #include "php.h"
 #include "zend_compile.h"
 #include "zend_cfg.h"
+#include "zend_func_info.h"
 #include "zend_worklist.h"
 
 static void zend_mark_reachable(zend_op *opcodes, zend_basic_block *blocks, zend_basic_block *b) /* {{{ */
@@ -75,7 +76,7 @@ static void zend_mark_reachable(zend_op *opcodes, zend_basic_block *blocks, zend
 }
 /* }}} */
 
-static void zend_mark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
+static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
 {
        zend_basic_block *blocks = cfg->blocks;
 
@@ -199,7 +200,7 @@ static void zend_mark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg, i
 }
 /* }}} */
 
-void zend_cfg_remark_reachable_blocks(zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
 {
        zend_basic_block *blocks = cfg->blocks;
        int i;
@@ -232,7 +233,7 @@ static void record_successor(zend_basic_block *blocks, int pred, int n, int succ
                block_map[i] = 1; \
        } while (0)
 
-int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants, int stackless, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */
+int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */
 {
        uint32_t flags = 0;
        uint32_t i;
@@ -270,7 +271,7 @@ int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants
                        case ZEND_YIELD:
                        case ZEND_YIELD_FROM:
                                flags |= ZEND_FUNC_TOO_DYNAMIC;
-                               if (stackless) {
+                               if (build_flags & ZEND_CFG_STACKLESS) {
                                        BB_START(i + 1);
                                }
                                break;
@@ -278,7 +279,7 @@ int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants
                        case ZEND_DO_UCALL:
                        case ZEND_DO_FCALL_BY_NAME:
                                flags |= ZEND_FUNC_HAS_CALLS;
-                               if (stackless) {
+                               if (build_flags & ZEND_CFG_STACKLESS) {
                                        BB_START(i + 1);
                                }
                                break;
@@ -532,7 +533,7 @@ int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants
        zend_mark_reachable_blocks(op_array, cfg, 0);
 
        if (func_flags) {
-               *func_flags = flags;
+               *func_flags |= flags;
        }
 
        return SUCCESS;
@@ -602,7 +603,7 @@ int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
 }
 /* }}} */
 
-int zend_cfg_compute_dominators_tree(zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
 {
        zend_basic_block *blocks = cfg->blocks;
        int blocks_count = cfg->blocks_count;
@@ -694,7 +695,7 @@ static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
 }
 /* }}} */
 
-int zend_cfg_identify_loops(zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */
+int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */
 {
        int i, j, k;
        int depth;
index 4bd2899346abeb7ce6d6edca9a2eba040066cadf..0aa1096aaa021dcf478de60bedd2b2ae5dda6e8e 100644 (file)
 #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()
 
index a8007ed7ea04e9071241db9262ef7609521b9816..d4fb28378696951830c8e8a411bfc1225bc670fd 100644 (file)
@@ -20,7 +20,7 @@
 #include "zend_compile.h"
 #include "zend_dfg.h"
 
-int zend_build_dfg(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg) /* {{{ */
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg) /* {{{ */
 {
        int set_size;
        zend_basic_block *blocks = cfg->blocks;
@@ -223,18 +223,6 @@ int zend_build_dfg(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg) /* {{{
                }
        } while (changed);
 
-//???D if (ZCG(accel_directives).jit_debug & JIT_DEBUG_DUMP_LIVENESS) {
-//???D         fprintf(stderr, "Variable Liveness\n");
-//???D         for (j = 0; j < blocks_count; j++) {
-//???D                 fprintf(stderr, "  BB%d:\n", j);
-//???D                 zend_jit_dump_var_set(op_array, "gen", dfg->gen + (j * dfg->size));
-//???D                 zend_jit_dump_var_set(op_array, "def", dfg->def + (j * dfg->size));
-//???D                 zend_jit_dump_var_set(op_array, "use", dfg->use + (j * dfg->size));
-//???D                 zend_jit_dump_var_set(op_array, "in ", dfg->in  + (j * dfg->size));
-//???D                 zend_jit_dump_var_set(op_array, "out", dfg->out + (j * dfg->size));
-//???D         }
-//???D }
-
        return SUCCESS;
 }
 /* }}} */
index 6fe8a1ab6df4343c1c88b0dd15a5b698d055c5dc..b6db009d060aef7ed1fbb8c38d6117087f33072f 100644 (file)
@@ -44,7 +44,7 @@ typedef struct _zend_dfg {
 
 BEGIN_EXTERN_C()
 
-int zend_build_dfg(zend_op_array *op_array, zend_cfg *cfg, zend_dfg *dfg);
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg);
 
 END_EXTERN_C()
 
index c7d5d95dd2cd3d2e312e70347e8a6f854fb25973..894cc0db15943376907067d021eb0352a69cadb8 100644 (file)
 #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");
+       }
+       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;
        }
-       fprintf(stderr, ": ; (lines=%d, args=%d, vars=%d, tmps=%d)\n",
+
+       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
index e879d950ebab1e99096ffd49713b569e0b46f11e..61ea538cefb73017c5efc1e0f4d2a0d44dcc2a9e 100644 (file)
 #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()
 
diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c
new file mode 100644 (file)
index 0000000..b4171b3
--- /dev/null
@@ -0,0 +1,1288 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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>                             |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id:$ */
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_extensions.h"
+#include "zend_ssa.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
+
+typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa);
+
+typedef struct _func_info_t {
+       const char *name;
+       int         name_len;
+       uint32_t    info;
+       info_func_t info_func;
+} func_info_t;
+
+#define F0(name, info) \
+       {name, sizeof(name)-1, (info), NULL}
+#define F1(name, info) \
+       {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | (info)), NULL}
+#define FN(name, info) \
+       {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
+#define FR(name, info) \
+       {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_REF | (info)), NULL}
+#define FX(name, info) \
+       {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | (info)), NULL}
+#define I1(name, info) \
+       {name, sizeof(name)-1, (MAY_BE_DEF | MAY_BE_RC1 | (info)), NULL}
+#define FC(name, callback) \
+       {name, sizeof(name)-1, 0, callback}
+
+static uint32_t zend_strlen_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+           call_info->num_args == 1) {
+
+               uint32_t tmp = MAY_BE_DEF | MAY_BE_RC1;
+               if (call_info->arg_info[0].opline) {
+                       uint32_t arg_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+
+                       if (arg_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) {
+                               tmp |= MAY_BE_LONG;
+                       }
+                       if (arg_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+                               /* warning, and returns NULL */
+                               tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+                       }
+                       if ((arg_info & MAY_BE_ANY) == MAY_BE_STRING) {
+                               /* TODO: strlen() may be overriden by mbstring */
+                               tmp |= FUNC_MAY_INLINE;
+                       }
+               } else {
+                       tmp |= MAY_BE_LONG | FUNC_MAY_WARN | MAY_BE_NULL;
+               }
+               return tmp;
+       } else {
+               /* warning, and returns NULL */
+               return FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_NULL;
+       }
+}
+
+static uint32_t zend_dechex_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+           call_info->num_args == 1) {
+               return MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_STRING;
+       } else {
+               /* warning, and returns NULL */
+               return FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_NULL;
+       }
+}
+
+static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+           (call_info->num_args == 2 || call_info->num_args == 3)) {
+
+               uint32_t t1 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+               uint32_t t2 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+               uint32_t t3 = 0;
+               uint32_t tmp = MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG;
+
+               if (call_info->num_args == 3) {
+                       t3 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline);
+               }
+               if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
+                       tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+                       tmp |= FUNC_MAY_WARN | MAY_BE_FALSE;
+               }
+               if ((t1 & MAY_BE_DOUBLE) || (t2 & MAY_BE_DOUBLE) || (t3 & MAY_BE_DOUBLE)) {
+                       tmp |= MAY_BE_ARRAY_OF_DOUBLE;
+                       tmp |= FUNC_MAY_WARN | MAY_BE_FALSE;
+               }
+               if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) {
+                       if (call_info->num_args == 2 && !(t3 & MAY_BE_DOUBLE)) {
+                               tmp |= MAY_BE_ARRAY_OF_LONG;
+                       }
+                       if (call_info->num_args == 3) {
+                               tmp |= FUNC_MAY_WARN | MAY_BE_FALSE;
+                       }
+               }
+               return tmp;
+       } else {
+               /* may warning, and return FALSE */
+               return FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+       }
+}
+
+static uint32_t zend_is_type_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+           call_info->num_args == 1) {
+               return MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_TRUE | FUNC_MAY_INLINE;
+       } else {
+               return MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_TRUE | FUNC_MAY_WARN;
+       }
+}
+
+static uint32_t zend_l_ss_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+           call_info->num_args == 2) {
+
+               uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+               uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+               uint32_t tmp = MAY_BE_DEF | MAY_BE_RC1;
+
+               if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+                   (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) {
+                       tmp |= MAY_BE_LONG;
+               }
+               if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+                   (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+                       /* warning, and returns NULL */
+                       tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+               }
+               return tmp;
+       } else {
+               /* warning, and returns NULL */
+               return FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_NULL | MAY_BE_LONG;
+       }
+}
+
+static uint32_t zend_lb_ssn_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+           call_info->num_args == 3) {
+               uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+               uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+               uint32_t arg3_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline);
+               uint32_t tmp = MAY_BE_DEF | MAY_BE_RC1;
+
+               if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+                   (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+                   (arg3_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) {
+                       tmp |= MAY_BE_LONG | MAY_BE_FALSE;
+               }
+               if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+                   (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+                   (arg3_info & (MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+                       /* warning, and returns NULL */
+                       tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+               }
+               return tmp;
+       } else {
+               /* warning, and returns NULL */
+               return FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_NULL | MAY_BE_LONG;
+       }
+}
+
+static uint32_t zend_b_s_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+           call_info->num_args == 1) {
+
+               uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+               uint32_t tmp = MAY_BE_DEF | MAY_BE_RC1;
+
+               if (arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) {
+                       tmp |= MAY_BE_FALSE | MAY_BE_TRUE;
+               }
+               if (arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+                       /* warning, and returns NULL */
+                       tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+               }
+               return tmp;
+       } else {
+               /* warning, and returns NULL */
+               return FUNC_MAY_WARN | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE;
+       }
+}
+
+#define UNKNOWN_INFO (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF)
+
+static const func_info_t func_infos[] = {
+       /* zend */
+       I1("zend_version",            MAY_BE_STRING),
+       I1("gc_collect_cycles",       MAY_BE_LONG),
+       I1("gc_enabled",              MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("gc_enable",               MAY_BE_NULL),
+       F1("gc_disable",              MAY_BE_NULL),
+       F1("func_num_args",           MAY_BE_LONG),
+       F1("func_get_arg",            UNKNOWN_INFO),
+       F1("func_get_args",           MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+       FC("strlen",                  zend_strlen_info),
+       FC("strcmp",                  zend_l_ss_info),
+       FC("strncmp",                 zend_lb_ssn_info),
+       FC("strcasecmp",              zend_l_ss_info),
+       FC("strncasecmp",             zend_lb_ssn_info),
+       F1("each",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("error_reporting",         MAY_BE_NULL | MAY_BE_LONG),
+       F1("define",                  MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_NULL), // TODO: inline
+       FC("defined",                 zend_b_s_info), // TODO: inline
+       F1("get_class",               MAY_BE_FALSE | MAY_BE_STRING),
+       F1("get_called_class",        MAY_BE_FALSE | MAY_BE_STRING),
+       F1("get_parrent_class",       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("is_subclass_of",          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline
+       F1("is_a",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline
+       F1("get_class_vars",          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+       F1("get_object_vars",         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+       F1("get_class_methods",       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("method_exists",           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("property_exists",         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("class_exists",            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("interface_exists",        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("trait_exists",            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       FC("function_exists",         zend_b_s_info), // TODO: inline
+       F1("class_alias",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("get_included_files",      MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("trigger_error",           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("user_error",              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("set_error_handler",       MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+       I1("restore_error_handler",   MAY_BE_TRUE),
+       F1("get_declared_traits",     MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("get_declared_classes",    MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("get_declared_interfaces", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("get_defined_functions",   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       I1("get_defined_vars",        MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+       F1("create_function",         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+       F1("get_resource_type",       MAY_BE_NULL | MAY_BE_STRING),
+       F1("get_defined_constants",   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_RESOURCE | MAY_BE_ARRAY_OF_ARRAY),
+       F1("debug_print_backtrace",   MAY_BE_NULL),
+       F1("debug_backtrace",         MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+       F1("get_loaded_extensions",   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       FC("extension_loaded",        zend_b_s_info),
+       F1("get_extension_funcs",     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+
+       /* ext/statdard */
+       F1("constant",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_RESOURCE),
+       F1("bin2hex",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("hex2bin",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("sleep",                        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("usleep",                       MAY_BE_NULL | MAY_BE_FALSE),
+#if HAVE_NANOSLEEP
+       F1("time_nanosleep",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("time_sleep_until",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_STRPTIME
+       F1("strptime",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+       F1("flush",                        MAY_BE_NULL),
+       F1("wordwrap",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("htmlspecialchars",             MAY_BE_NULL | MAY_BE_STRING),
+       F1("htmlentities",                 MAY_BE_NULL | MAY_BE_STRING),
+       F1("html_entity_decode",           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("htmlspecialchars_decode",      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("get_html_translation_table",   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+       F1("sha1",                         MAY_BE_NULL | MAY_BE_STRING),
+       F1("sha1_file",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("md5",                          MAY_BE_NULL | MAY_BE_STRING),
+       F1("md5_file",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("crc32",                        MAY_BE_NULL | MAY_BE_LONG),
+       F1("iptcparse",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("iptcembed",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("getimagesize",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("getimagesizefromstring",       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("image_type_to_mime_type",      MAY_BE_NULL | MAY_BE_STRING),
+       F1("image_type_to_extension",      MAY_BE_FALSE | MAY_BE_STRING),
+       F1("phpinfo",                      MAY_BE_NULL | MAY_BE_TRUE),
+       F1("phpversion",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("phpcredits",                   MAY_BE_NULL | MAY_BE_TRUE),
+       F1("php_sapi_name",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("php_uname",                    MAY_BE_NULL | MAY_BE_STRING),
+       F1("php_ini_scanned_files",        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("php_ini_loaded_file",          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("strnatcmp",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("strnatcasecmp",                MAY_BE_NULL | MAY_BE_LONG),
+       F1("substr_count",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("strspn",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("strcspn",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("strtok",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("strtoupper",                   MAY_BE_NULL | MAY_BE_STRING),
+       F1("strtolower",                   MAY_BE_NULL | MAY_BE_STRING),
+       F1("strpos",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("stripos",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("strrpos",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("strripos",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("strrev",                       MAY_BE_NULL | MAY_BE_STRING),
+       F1("hebrev",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("hebrevc",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("nl2br",                        MAY_BE_NULL | MAY_BE_STRING),
+       F1("basename",                     MAY_BE_NULL | MAY_BE_STRING),
+       F1("dirname",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("pathinfo",                     MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+       F1("stripslashes",                 MAY_BE_NULL | MAY_BE_STRING),
+       F1("stripcslashes",                MAY_BE_NULL | MAY_BE_STRING),
+       F1("strstr",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("stristr",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("strrchr",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("str_shuffle",                  MAY_BE_NULL | MAY_BE_STRING),
+       F1("str_word_count",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("str_split",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("strpbrk",                      MAY_BE_FALSE | MAY_BE_STRING),
+       F1("substr_compare",               MAY_BE_FALSE | MAY_BE_LONG),
+#ifdef HAVE_STRCOLL
+       F1("strcoll",                      MAY_BE_NULL | MAY_BE_LONG),
+#endif
+#ifdef HAVE_STRFMON
+       F1("money_format",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+       F1("substr",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("substr_replace",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+       F1("quotemeta",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ucfirst",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("lcfirst",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("ucwords",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("strtr",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("addslashes",                   MAY_BE_NULL | MAY_BE_STRING),
+       F1("addcslashes",                  MAY_BE_NULL | MAY_BE_STRING),
+       F1("rtrim",                        MAY_BE_NULL | MAY_BE_STRING),
+       F1("str_replace",                  MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+       F1("str_ireplace",                 MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+       F1("str_repeat",                   MAY_BE_NULL | MAY_BE_STRING),
+       F1("count_chars",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+       F1("chunk_split",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("trim",                         MAY_BE_NULL | MAY_BE_STRING),
+       F1("ltrim",                        MAY_BE_NULL | MAY_BE_STRING),
+       F1("strip_tags",                   MAY_BE_NULL | MAY_BE_STRING),
+       F1("similar_text",                 MAY_BE_NULL | MAY_BE_LONG),
+       F1("explode",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("implode",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("join",                         MAY_BE_NULL | MAY_BE_STRING),
+       F1("setlocale",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("localeconv",                   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if HAVE_NL_LANGINFO
+       F1("nl_langinfo",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+       F1("soundex",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("levenshtein",                  MAY_BE_NULL | MAY_BE_LONG),
+       F1("chr",                          MAY_BE_NULL | MAY_BE_STRING),
+       F1("ord",                          MAY_BE_NULL | MAY_BE_LONG),
+       F1("parse_str",                    MAY_BE_NULL),
+       F1("str_getcsv",                   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+       F1("str_pad",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("chop",                         MAY_BE_NULL | MAY_BE_STRING),
+       F1("strchr",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("sprintf",                      MAY_BE_FALSE | MAY_BE_STRING),
+       F1("printf",                       MAY_BE_FALSE | MAY_BE_LONG),
+       F1("vprintf",                      MAY_BE_FALSE | MAY_BE_LONG),
+       F1("vsprintf",                     MAY_BE_FALSE | MAY_BE_STRING),
+       F1("fprintf",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("vfprintf",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("sscanf",                       MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+       F1("fscanf",                       MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+       F1("parse_url",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG),
+       F1("urlencode",                    MAY_BE_NULL | MAY_BE_STRING),
+       F1("urldecode",                    MAY_BE_NULL | MAY_BE_STRING),
+       F1("rawurlencode",                 MAY_BE_NULL | MAY_BE_STRING),
+       F1("rawurldecode",                 MAY_BE_NULL | MAY_BE_STRING),
+       F1("http_build_query",             MAY_BE_FALSE | MAY_BE_STRING),
+#if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
+       F1("readlink",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("linkinfo",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("symlink",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("link",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("unlink",                       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("exec",                         MAY_BE_FALSE | MAY_BE_STRING),
+       F1("system",                       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("escapeshellcmd",               MAY_BE_NULL | MAY_BE_STRING),
+       F1("escapeshellarg",               MAY_BE_NULL | MAY_BE_STRING),
+       F1("passthru",                     MAY_BE_FALSE | MAY_BE_STRING),
+       F1("shell_exec",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#ifdef PHP_CAN_SUPPORT_PROC_OPEN
+       F1("proc_open",                    MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("proc_close",                   MAY_BE_FALSE | MAY_BE_LONG),
+       F1("proc_terminate",               MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("proc_get_status",              MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+#ifdef HAVE_NICE
+       F1("proc_nice",                    MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("rand",                         MAY_BE_NULL | MAY_BE_LONG),
+       F1("srand",                        MAY_BE_NULL),
+       F1("getrandmax",                   MAY_BE_NULL | MAY_BE_LONG),
+       F1("mt_rand",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mt_srand",                     MAY_BE_NULL),
+       F1("mt_getrandmax",                MAY_BE_NULL | MAY_BE_LONG),
+#if HAVE_GETSERVBYNAME
+       F1("getservbyname",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+#endif
+#if HAVE_GETSERVBYPORT
+       F1("getservbyport",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if HAVE_GETPROTOBYNAME
+       F1("getprotobyname",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+#endif
+#if HAVE_GETPROTOBYNUMBER
+       F1("getprotobynumber",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+       F1("getmyuid",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("getmygid",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("getmypid",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("getmyinode",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("getlastmod",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("base64_decode",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("base64_encode",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("password_hash",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("password_get_info",            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("password_needs_rehash",        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("password_verify",              MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("convert_uuencode",             MAY_BE_FALSE | MAY_BE_STRING),
+       F1("convert_uudecode",             MAY_BE_FALSE | MAY_BE_STRING),
+       F1("abs",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+       F1("ceil",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+       F1("floor",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+       F1("round",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+       F1("sin",                          MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("cos",                          MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("tan",                          MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("asin",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("acos",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("atan",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("atanh",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("atan2",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("sinh",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("cosh",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("tanh",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("asinh",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("acosh",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("expm1",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("log1p",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("pi",                           MAY_BE_DOUBLE),
+       F1("is_finite",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_nan",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_infinite",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("pow",                          MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+       F1("exp",                          MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("log",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+       F1("log10",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("sqrt",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("hypot",                        MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("deg2rad",                      MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("rad2deg",                      MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("bindec",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+       F1("hexdec",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+       F1("octdec",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+       F1("decbin",                       MAY_BE_NULL | MAY_BE_STRING),
+       F1("decoct",                       MAY_BE_NULL | MAY_BE_STRING),
+       FC("dechex",                       zend_dechex_info),
+       F1("base_convert",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("number_format",                MAY_BE_NULL | MAY_BE_STRING),
+       F1("fmod",                         MAY_BE_NULL | MAY_BE_DOUBLE),
+#ifdef HAVE_INET_NTOP
+       F1("inet_ntop",                    MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#ifdef HAVE_INET_PTON
+       F1("inet_pton",                    MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+       F1("ip2long",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("long2ip",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("getenv",                       MAY_BE_FALSE | MAY_BE_STRING),
+#ifdef HAVE_PUTENV
+       F1("putenv",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("getopt",                       MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#ifdef HAVE_GETLOADAVG
+       F1("sys_getloadavg",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+       F1("microtime",                    MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING),
+       F1("gettimeofday",                 MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING),
+#endif
+#ifdef HAVE_GETRUSAGE
+       F1("getrusage",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+       F1("uniqid",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+       F1("quoted_printable_decode",      MAY_BE_NULL | MAY_BE_STRING),
+       F1("quoted_printable_encode",      MAY_BE_NULL | MAY_BE_STRING),
+       F1("convert_cyr_string",           MAY_BE_NULL | MAY_BE_STRING),
+       F1("get_current_user",             MAY_BE_NULL | MAY_BE_STRING),
+       F1("set_time_limit",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("header_register_callback",     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("get_cfg_var",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("magic_quotes_runtime",         MAY_BE_NULL | MAY_BE_FALSE),
+       F1("set_magic_quotes_runtime",     MAY_BE_NULL | MAY_BE_FALSE),
+       F1("get_magic_quotes_gpc",         MAY_BE_NULL | MAY_BE_FALSE),
+       F1("get_magic_quotes_runtime",     MAY_BE_NULL | MAY_BE_FALSE),
+       F1("error_log",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("error_get_last",               MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("call_user_func",               UNKNOWN_INFO),
+       F1("call_user_func_array",         UNKNOWN_INFO),
+       F1("call_user_method",             UNKNOWN_INFO),
+       F1("call_user_method_array",       UNKNOWN_INFO),
+       F1("forward_static_call",          UNKNOWN_INFO),
+       F1("forward_static_call_array",    UNKNOWN_INFO),
+       F1("serialize",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       FX("unserialize",                  UNKNOWN_INFO),
+       F1("var_dump",                     MAY_BE_NULL),
+       F1("var_export",                   MAY_BE_NULL | MAY_BE_STRING),
+       F1("debug_zval_dump",              MAY_BE_NULL),
+       F1("print_r",                      MAY_BE_FALSE | MAY_BE_STRING),
+       F1("memory_get_usage",             MAY_BE_FALSE | MAY_BE_LONG),
+       F1("memory_get_peak_usage",        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("register_shutdown_function",   MAY_BE_NULL | MAY_BE_FALSE),
+       F1("register_tick_function",       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("unregister_tick_function",     MAY_BE_NULL),
+       F1("highlight_file",               MAY_BE_FALSE | MAY_BE_STRING),
+       F1("show_source",                  MAY_BE_FALSE | MAY_BE_STRING),
+       F1("highlight_string",             MAY_BE_FALSE | MAY_BE_STRING),
+       F1("php_strip_whitespace",         MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ini_get",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ini_get_all",                  MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("ini_set",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ini_alter",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ini_restore",                  MAY_BE_NULL),
+       F1("get_include_path",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("set_include_path",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("restore_include_path",         MAY_BE_NULL),
+       F1("setcookie",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("setrawcookie",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("header",                       MAY_BE_NULL),
+       F1("header_remove",                MAY_BE_NULL),
+       F1("headers_sent",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("headers_list",                 MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("http_response_code",           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("connection_aborted",           MAY_BE_LONG),
+       F1("connection_status",            MAY_BE_LONG),
+       F1("ignore_user_abort",            MAY_BE_NULL | MAY_BE_LONG),
+       F1("parse_ini_file",               MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("parse_ini_string",             MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if ZEND_DEBUG
+       F1("config_get_hash",              MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#endif
+       F1("is_uploaded_file",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("move_uploaded_file",           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("gethostbyaddr",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gethostbyname",                MAY_BE_NULL | MAY_BE_STRING),
+       F1("gethostbynamel",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GETHOSTNAME
+       F1("gethostname",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if defined(PHP_WIN32) || (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE)))
+       F1("dns_check_record",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("checkdnsrr",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS
+       F1("dns_get_mx",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("getmxrr",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("dns_get_record",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+# endif
+#endif
+       F1("intval",                       MAY_BE_NULL | MAY_BE_LONG),
+       F1("floatval",                     MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("doubleval",                    MAY_BE_NULL | MAY_BE_DOUBLE),
+       F1("strval",                       MAY_BE_NULL | MAY_BE_STRING),
+       F1("boolval",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("gettype",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("settype",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       FC("is_null",                      zend_is_type_info),
+       F1("is_resource",                  MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for closed resources
+       FC("is_bool",                      zend_is_type_info),
+       FC("is_long",                      zend_is_type_info),
+       FC("is_float",                     zend_is_type_info),
+       FC("is_int",                       zend_is_type_info),
+       FC("is_integer",                   zend_is_type_info),
+       FC("is_double",                    zend_is_type_info),
+       FC("is_real",                      zend_is_type_info),
+       F1("is_numeric",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       FC("is_string",                    zend_is_type_info),
+       FC("is_array",                     zend_is_type_info),
+       F1("is_object",                    MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for incomplete class
+       F1("is_scalar",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_callable",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("pclose",                       MAY_BE_FALSE | MAY_BE_LONG),
+       F1("popen",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("readfile",                     MAY_BE_FALSE | MAY_BE_LONG),
+       F1("rewind",                       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("rmdir",                        MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("umask",                        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("fclose",                       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("feof",                         MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("fgetc",                        MAY_BE_FALSE | MAY_BE_STRING),
+       F1("fgets",                        MAY_BE_FALSE | MAY_BE_STRING),
+       F1("fgetss",                       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("fread",                        MAY_BE_FALSE | MAY_BE_STRING),
+       F1("fopen",                        MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("fpassthru",                    MAY_BE_FALSE | MAY_BE_LONG),
+       F1("ftruncate",                    MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("fstat",                        MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+       F1("fseek",                        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("ftell",                        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("fflush",                       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("fwrite",                       MAY_BE_FALSE | MAY_BE_LONG),
+       F1("fputs",                        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mkdir",                        MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("rename",                       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("copy",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("tempnam",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("tmpfile",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("file",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("file_get_contents",            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("file_put_contents",            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("stream_select",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("stream_context_create",        MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_context_set_params",    MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_context_get_params",    MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+       F1("stream_context_set_option",    MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_context_get_options",   MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+       F1("stream_context_get_default",   MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_context_set_default",   MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_filter_prepend",        MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_filter_append",         MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_filter_remove",         MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_socket_client",         MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_socket_server",         MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_socket_accept",         MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("stream_socket_get_name",       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("stream_socket_recvfrom",       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("stream_socket_sendto",         MAY_BE_FALSE | MAY_BE_LONG),
+       F1("stream_socket_enable_crypto",  MAY_BE_FALSE | MAY_BE_LONG),
+#ifdef HAVE_SHUTDOWN
+       F1("stream_socket_shutdown",       MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_SOCKETPAIR
+       F1("stream_socket_pair",           MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_RESOURCE),
+#endif
+       F1("stream_copy_to_stream",        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("stream_get_contents",          MAY_BE_FALSE | MAY_BE_STRING),
+       F1("stream_supports_lock",         MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("fgetcsv",                      MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+       F1("fputcsv",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("flock",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("get_meta_tags",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+       F1("stream_set_read_buffer",       MAY_BE_FALSE | MAY_BE_LONG),
+       F1("stream_set_write_buffer",      MAY_BE_FALSE | MAY_BE_LONG),
+       F1("set_file_buffer",              MAY_BE_FALSE | MAY_BE_LONG),
+       F1("stream_set_chunk_size",        MAY_BE_FALSE | MAY_BE_LONG),
+       F1("set_socket_blocking",          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_set_blocking",          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("socket_set_blocking",          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_get_meta_data",         MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+       F1("stream_get_line",              MAY_BE_FALSE | MAY_BE_STRING),
+       F1("stream_wrapper_register",      MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_register_wrapper",      MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_wrapper_unregister",    MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_wrapper_restore",       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_get_wrappers",          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("stream_get_transports",        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("stream_resolve_include_path",  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("stream_is_local",              MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("get_headers",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if HAVE_SYS_TIME_H || defined(PHP_WIN32)
+       F1("stream_set_timeout",           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("socket_set_timeout",           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("socket_get_status",            MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+#if (!defined(__BEOS__) && !defined(NETWARE) && HAVE_REALPATH) || defined(ZTS)
+       F1("realpath",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#ifdef HAVE_FNMATCH
+       F1("fnmatch",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("fsockopen",                    MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("pfsockopen",                   MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("pack",                         MAY_BE_FALSE | MAY_BE_STRING),
+       F1("unpack",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+       F1("get_browser",                  MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+#if HAVE_CRYPT
+       F1("crypt",                        MAY_BE_NULL | MAY_BE_STRING),
+#endif
+       F1("opendir",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("closedir",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("chdir",                        MAY_BE_FALSE | MAY_BE_TRUE),
+#if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
+       F1("chroot",                       MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("getcwd",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("rewinddir",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("readdir",                      MAY_BE_FALSE | MAY_BE_STRING),
+       F1("dir",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("scandir",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GLOB
+       F1("glob",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+       F1("fileatime",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("filectime",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("filegroup",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("fileinode",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("filemtime",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("fileowner",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("fileperms",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("filesize",                     MAY_BE_NULL | MAY_BE_LONG),
+       F1("filetype",                     MAY_BE_NULL | MAY_BE_STRING),
+       F1("file_exists",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_writable",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_writeable",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_readable",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_executable",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_file",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_dir",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("is_link",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stat",                         MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("lstat",                        MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifndef NETWARE
+       F1("chown",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("chgrp",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_LCHOWN
+       F1("lchown",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_LCHOWN
+       F1("lchgrp",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("chmod",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#if HAVE_UTIME
+       F1("touch",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("clearstatcache",               MAY_BE_NULL),
+       F1("disk_total_space",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+       F1("disk_free_space",              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+       F1("diskfreespace",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+       F1("realpath_cache_size",          MAY_BE_NULL | MAY_BE_LONG),
+       F1("realpath_cache_get",           MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
+       F1("mail",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("ezmlm_hash",                   MAY_BE_NULL | MAY_BE_LONG),
+#ifdef HAVE_SYSLOG_H
+       F1("openlog",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("syslog",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("closelog",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+       F1("lcg_value",                    MAY_BE_DOUBLE),
+       F1("metaphone",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ob_start",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("ob_flush",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("ob_clean",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("ob_end_flush",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("ob_end_clean",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("ob_get_flush",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ob_get_clean",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ob_get_length",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("ob_get_level",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("ob_get_status",                MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("ob_get_contents",              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ob_implicit_flush",            MAY_BE_NULL),
+       F1("ob_list_handlers",             MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("ksort",                        MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("krsort",                       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("natsort",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("natcasesort",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("asort",                        MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("arsort",                       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("sort",                         MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("rsort",                        MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("usort",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("uasort",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("uksort",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("shuffle",                      MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("array_walk",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("array_walk_recursive",         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("count",                        MAY_BE_NULL | MAY_BE_LONG),
+       F1("end",                          UNKNOWN_INFO),
+       F1("prev",                         UNKNOWN_INFO),
+       F1("next",                         UNKNOWN_INFO),
+       F1("reset",                        UNKNOWN_INFO),
+       F1("current",                      UNKNOWN_INFO),
+       F1("key",                          MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING),
+       F1("min",                          UNKNOWN_INFO),
+       F1("max",                          UNKNOWN_INFO),
+       F1("in_array",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("array_search",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+       F1("extract",                      MAY_BE_NULL | MAY_BE_LONG),
+       F1("compact",                      MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_fill",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+       F1("array_fill_keys",              MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       FC("range",                        zend_range_info),
+       F1("array_multisort",              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("array_push",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("array_pop",                    UNKNOWN_INFO),
+       F1("array_shift",                  UNKNOWN_INFO),
+       F1("array_unshift",                MAY_BE_NULL | MAY_BE_LONG),
+       F1("array_splice",                 MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_slice",                  MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_merge",                  MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_merge_recursive",        MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_replace",                MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_replace_recursive",      MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_keys",                   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("array_values",                 MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_count_values",           MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+       F1("array_column",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_reverse",                MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_reduce",                 UNKNOWN_INFO),
+       F1("array_pad",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_flip",                   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("array_change_key_case",        MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_rand",                   UNKNOWN_INFO),
+       F1("array_unique",                 MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_intersect",              MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_intersect_key",          MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_intersect_ukey",         MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_uintersect",             MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_intersect_assoc",        MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_uintersect_assoc",       MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_intersect_uassoc",       MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_uintersect_uassoc",      MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_diff",                   MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_diff_key",               MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_diff_ukey",              MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_udiff",                  MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_diff_assoc",             MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_udiff_assoc",            MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_diff_uassoc",            MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_udiff_uassoc",           MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_sum",                    MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+       F1("array_product",                MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+       F1("array_filter",                 MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_map",                    MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_chunk",                  MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_combine",                MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("array_key_exists",             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("pos",                          UNKNOWN_INFO),
+       F1("sizeof",                       MAY_BE_NULL | MAY_BE_LONG),
+       F1("key_exists",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("assert",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("assert_options",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+       F1("version_compare",              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG),
+#if HAVE_FTOK
+       F1("ftok",                         MAY_BE_NULL | MAY_BE_LONG),
+#endif
+       F1("str_rot13",                    MAY_BE_NULL | MAY_BE_STRING),
+       F1("stream_get_filters",           MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("stream_filter_register",       MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("stream_bucket_prepend",        MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("stream_bucket_append",         MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("stream_bucket_new",            MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("output_add_rewrite_var",       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("output_reset_rewrite_vars",    MAY_BE_FALSE),
+       F1("sys_get_temp_dir",             MAY_BE_NULL | MAY_BE_STRING),
+
+       /* ext/date */
+       F1("strtotime",                             MAY_BE_FALSE | MAY_BE_LONG),
+       F1("date",                                  MAY_BE_FALSE | MAY_BE_STRING),
+       F1("idate",                                 MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gmdate",                                MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mktime",                                MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gmmktime",                              MAY_BE_FALSE | MAY_BE_LONG),
+       F1("checkdate",                             MAY_BE_FALSE | MAY_BE_TRUE),
+#ifdef HAVE_STRFTIME
+       F1("strftime",                              MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gmstrftime",                            MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+       F1("time",                                  MAY_BE_LONG),
+       F1("localtime",                             MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+       F1("getdate",                               MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("date_create",                           MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_create_immutable",                 MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_create_from_format",               MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_create_immutable_from_format",     MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_parse",                            MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+       F1("date_parse_from_format",                MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+       F1("date_get_last_errors",                  MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_ARRAY),
+       F1("date_format",                           MAY_BE_FALSE | MAY_BE_STRING),
+       F1("date_modify",                           MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_add",                              MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_sub",                              MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_timezone_get",                     MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_timezone_set",                     MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_offset_get",                       MAY_BE_FALSE | MAY_BE_LONG),
+       F1("date_diff",                             MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_time_set",                         MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_date_set",                         MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_isodate_set",                      MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_timestamp_set",                    MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_timestamp_get",                    MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("timezone_open",                         MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("timezone_name_get",                     MAY_BE_FALSE | MAY_BE_STRING),
+       F1("timezone_name_from_abbr",               MAY_BE_FALSE | MAY_BE_STRING),
+       F1("timezone_offset_get",                   MAY_BE_FALSE | MAY_BE_LONG),
+       F1("timezone_transitions_get",              MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+       F1("timezone_location_get",                 MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
+       F1("timezone_identifiers_list",             MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("timezone_abbreviations_list",           MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("timezone_version_get",                  MAY_BE_STRING),
+       F1("date_interval_create_from_date_string", MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("date_interval_format",                  MAY_BE_FALSE | MAY_BE_STRING),
+       F1("date_default_timezone_set",             MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("date_default_timezone_get",             MAY_BE_STRING),
+       F1("date_sunrise",                          MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+       F1("date_sunset",                           MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+       F1("date_sun_info",                         MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG),
+
+       /* ext/preg */
+       F1("preg_match",                                            MAY_BE_FALSE | MAY_BE_LONG),
+       F1("preg_match_all",                                MAY_BE_FALSE | MAY_BE_LONG),
+       F1("preg_replace",                                      MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+       F1("preg_replace_callback",                     MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+       F1("preg_filter",                                           MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+       F1("preg_split",                                            MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("preg_quote",                                            MAY_BE_NULL | MAY_BE_STRING),
+       F1("preg_grep",                                         MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+       F1("preg_last_error",                               MAY_BE_NULL | MAY_BE_LONG),
+
+       /* ext/ereg */
+       F1("ereg",                                              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("ereg_replace",                              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("eregi",                                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("eregi_replace",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("split",                                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("spliti",                                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("sql_regcase",                                   MAY_BE_NULL | MAY_BE_STRING),
+
+       /* ext/mysql */
+       F1("mysql_connect",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_pconnect",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_close",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_select_db",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_create_db",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_drop_db",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_query",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+       F1("mysql_unbuffered_query",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+       F1("mysql_db_query",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+       F1("mysql_list_dbs",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_list_tables",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_list_fields",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_list_processes",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_error",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_errno",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_affected_rows",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_insert_id",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_result",                                                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_num_rows",                                            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_num_fields",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_fetch_row",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+       F1("mysql_fetch_array",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+       F1("mysql_fetch_assoc",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+       F1("mysql_fetch_object",                                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("mysql_data_seek",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_fetch_lengths",                                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+       F1("mysql_fetch_field",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("mysql_field_seek",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_free_result",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_field_name",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_field_table",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_field_len",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_field_type",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_field_flags",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_escape_string",                                       MAY_BE_NULL | MAY_BE_STRING),
+       F1("mysql_real_escape_string",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_stat",                                                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_thread_id",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_client_encoding",                                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_ping",                                                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_get_client_info",                                     MAY_BE_NULL | MAY_BE_STRING),
+       F1("mysql_get_host_info",                                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_get_proto_info",                                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_get_server_info",                                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_info",                                                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_set_charset",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql",                                                                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_fieldname",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_fieldtable",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_fieldlen",                                            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_fieldtype",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_fieldflags",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_selectdb",                                            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_createdb",                                            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_dropdb",                                                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_freeresult",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mysql_numfields",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_numrows",                                                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mysql_listdbs",                                                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_listtables",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_listfields",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("mysql_db_name",                                                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_dbname",                                                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_tablename",                                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mysql_table_name",                                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+
+       /* ext/curl */
+       F1("curl_init",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("curl_copy_handle",                      MAY_BE_NULL | MAY_BE_RESOURCE),
+       F1("curl_version",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("curl_setopt",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("curl_setopt_array",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("curl_exec",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("curl_getinfo",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+       F1("curl_error",                            MAY_BE_NULL | MAY_BE_STRING),
+       F1("curl_errno",                            MAY_BE_NULL | MAY_BE_LONG),
+       F1("curl_close",                            MAY_BE_NULL),
+       F1("curl_strerror",                         MAY_BE_NULL | MAY_BE_STRING),
+       F1("curl_multi_strerror",                   MAY_BE_NULL | MAY_BE_STRING),
+       F1("curl_reset",                            MAY_BE_NULL),
+       F1("curl_escape",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("curl_unescape",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("curl_pause",                            MAY_BE_NULL | MAY_BE_LONG),
+       F1("curl_multi_init",                       MAY_BE_NULL | MAY_BE_RESOURCE),
+       F1("curl_multi_add_handle",                 MAY_BE_NULL | MAY_BE_LONG),
+       F1("curl_multi_remove_handle",              MAY_BE_NULL | MAY_BE_LONG),
+       F1("curl_multi_select",                     MAY_BE_NULL | MAY_BE_LONG),
+       F1("curl_multi_exec",                       MAY_BE_NULL | MAY_BE_LONG),
+       F1("curl_multi_getcontent",                 MAY_BE_NULL | MAY_BE_STRING),
+       F1("curl_multi_info_read",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_RESOURCE),
+       F1("curl_multi_close",                      MAY_BE_NULL),
+       F1("curl_multi_setopt",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("curl_share_init",                       MAY_BE_NULL | MAY_BE_RESOURCE),
+       F1("curl_share_close",                      MAY_BE_NULL),
+       F1("curl_share_setopt",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("curl_file_create",                      MAY_BE_OBJECT),
+
+       /* ext/mbstring */
+       F1("mb_convert_case",                       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_strtoupper",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_strtolower",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_language",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_internal_encoding",                  MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_http_input",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_http_output",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_detect_order",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("mb_substitute_character",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+       F1("mb_parse_str",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mb_output_handler",                     MAY_BE_NULL | MAY_BE_STRING),
+       F1("mb_preferred_mime_name",                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_strlen",                             MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_strpos",                             MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_strrpos",                            MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_stripos",                            MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_strripos",                           MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_strstr",                             MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_strrchr",                            MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_stristr",                            MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_strrichr",                           MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_substr_count",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_substr",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_strcut",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_strwidth",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_strimwidth",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_convert_encoding",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_detect_encoding",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_list_encodings",                     MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("mb_encoding_aliases",                   MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("mb_convert_kana",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_encode_mimeheader",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_decode_mimeheader",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_convert_variables",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_encode_numericentity",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_decode_numericentity",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_send_mail",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mb_get_info",                           MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+       F1("mb_check_encoding",                     MAY_BE_FALSE | MAY_BE_TRUE),
+
+       F1("mb_regex_encoding",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_regex_set_options",                  MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_ereg",                               MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_eregi",                              MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mb_ereg_replace",                       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_eregi_replace",                      MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_ereg_replace_callback",              MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mb_split",                              MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("mb_ereg_match",                         MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mb_ereg_search",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mb_ereg_search_pos",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+       F1("mb_ereg_search_regs",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+       F1("mb_ereg_search_init",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mb_ereg_search_getregs",                MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+       F1("mb_ereg_search_getpos",                 MAY_BE_LONG),
+       F1("mb_ereg_search_setpos",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+
+       F1("mbregex_encoding",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mbereg",                                MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mberegi",                               MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mbereg_replace",                        MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mberegi_replace",                       MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mbsplit",                               MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("mbereg_match",                          MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mbereg_search",                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mbereg_search_pos",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+       F1("mbereg_search_regs",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+       F1("mbereg_search_init",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("mbereg_search_getregs",                 MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+       F1("mbereg_search_getpos",                  MAY_BE_LONG),
+       F1("mbereg_search_setpos",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+
+       /* ext/iconv */
+       F1("iconv",                                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("iconv_get_encoding",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+       F1("iconv_set_encoding",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("iconv_strlen",                          MAY_BE_FALSE | MAY_BE_LONG),
+       F1("iconv_substr",                          MAY_BE_FALSE | MAY_BE_STRING),
+       F1("iconv_strpos",                          MAY_BE_FALSE | MAY_BE_LONG),
+       F1("iconv_strrpos",                         MAY_BE_FALSE | MAY_BE_LONG),
+       F1("iconv_mime_encode",                     MAY_BE_FALSE | MAY_BE_STRING),
+       F1("iconv_mime_decode",                     MAY_BE_FALSE | MAY_BE_STRING),
+       F1("iconv_mime_decode_headers",             MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+
+       /* ext/json */
+       F1("json_encode",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("json_decode",                           MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+       F1("json_last_error",                       MAY_BE_NULL | MAY_BE_LONG),
+       F1("json_last_error_msg",                   MAY_BE_NULL | MAY_BE_STRING),
+
+       /* ext/xml */
+       F1("xml_parser_create",                     MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("xml_parser_create_ns",                  MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("xml_set_object",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_element_handler",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_character_data_handler",        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_processing_instruction_handler",MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_default_handler",               MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_unparsed_entity_decl_handler",  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_notation_decl_handler",         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_external_entity_ref_handler",   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_start_namespace_decl_handler",  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_set_end_namespace_decl_handler",    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_parse",                             MAY_BE_NULL | MAY_BE_LONG),
+       F1("xml_parse_into_struct",                 MAY_BE_NULL | MAY_BE_LONG),
+       F1("xml_get_error_code",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("xml_error_string",                      MAY_BE_NULL | MAY_BE_STRING),
+       F1("xml_get_current_line_number",           MAY_BE_NULL | MAY_BE_LONG),
+       F1("xml_get_current_column_number",         MAY_BE_NULL | MAY_BE_LONG),
+       F1("xml_get_current_byte_index",            MAY_BE_NULL | MAY_BE_LONG),
+       F1("xml_parser_free",                       MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_parser_set_option",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("xml_parser_get_option",                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+       F1("utf8_encode",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("utf8_decode",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+
+       /* ext/zlib */
+       F1("readgzfile",                            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gzrewind",                              MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("gzclose",                               MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("gzeof",                                 MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("gzgetc",                                MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzgets",                                MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzgetss",                               MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzread",                                MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzopen",                                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("gzpassthru",                            MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gzseek",                                MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gztell",                                MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gzwrite",                               MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gzputs",                                MAY_BE_FALSE | MAY_BE_LONG),
+       F1("gzfile",                                MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("gzcompress",                            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzuncompress",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzdeflate",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzinflate",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzencode",                              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("gzdecode",                              MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("zlib_encode",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("zlib_decode",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("zlib_get_coding_type",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("ob_gzhandler",                          MAY_BE_FALSE | MAY_BE_STRING),
+
+       /* ext/hash */
+       F1("hash",                                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("hash_file",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("hash_hmac",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("hash_hmac_file",                        MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("hash_init",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("hash_update",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("hash_update_stream",                    MAY_BE_NULL | MAY_BE_LONG),
+       F1("hash_update_file",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+       F1("hash_final",                            MAY_BE_NULL | MAY_BE_STRING),
+       F1("hash_copy",                             MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("hash_algos",                            MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+       F1("hash_pbkdf2",                           MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mhash_keygen_s2k",                      MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mhash_get_block_size",                  MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+       F1("mhash_get_hash_name",                   MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+       F1("mhash_count",                           MAY_BE_NULL | MAY_BE_LONG),
+       F1("mhash",                                 MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+};
+
+static HashTable func_info;
+int zend_func_info_rid = -1;
+
+uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+       uint32_t ret = 0;
+
+       if (call_info->callee_func->type == ZEND_INTERNAL_FUNCTION) {
+               func_info_t *info;
+
+               if ((info = zend_hash_find_ptr(&func_info, Z_STR_P(CRT_CONSTANT_EX((zend_op_array*)call_info->callee_func, call_info->caller_init_opline->op2, ssa->rt_constants)))) != NULL) {
+                       if (info->info_func) {
+                               ret = info->info_func(call_info, ssa);
+                       } else {
+                               ret = info->info;
+                       }
+#if 0
+               } else {
+                       fprintf(stderr, "Unknown internal function '%s'\n", func->common.function_name);
+#endif
+               }
+       } else {
+               // FIXME: the order of functions matters!!!
+               zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)call_info->callee_func);
+               if (info) {
+                       ret = info->return_info.type;
+               }
+       }
+       if (!ret) {
+               ret = MAY_BE_DEF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+               if (call_info->callee_func->type == ZEND_INTERNAL_FUNCTION) {
+                       ret |= FUNC_MAY_WARN;
+               }
+               if (call_info->callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+                       ret |= MAY_BE_REF;
+               } else {
+                       ret |= MAY_BE_RC1 | MAY_BE_RCN;
+               }
+       }
+       return ret;
+}
+
+int zend_func_info_startup(void)
+{
+       zend_extension dummy;
+       int i;
+
+       if (zend_func_info_rid == -1) {
+               zend_func_info_rid = zend_get_resource_handle(&dummy);
+               if (zend_func_info_rid < 0) {
+                       return FAILURE;
+               }
+
+               zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1);
+               for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) {
+                       if (zend_hash_str_add_ptr(&func_info, func_infos[i].name, func_infos[i].name_len, (void**)&func_infos[i]) == NULL) {
+                               fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
+                       }
+               }
+       }
+
+       return SUCCESS;
+}
+
+int zend_func_info_shutdown(void)
+{
+       if (zend_func_info_rid != -1) {
+               zend_hash_destroy(&func_info);
+               zend_func_info_rid = -1;
+       }
+       return SUCCESS;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_func_info.h b/ext/opcache/Optimizer/zend_func_info.h
new file mode 100644 (file)
index 0000000..c520a68
--- /dev/null
@@ -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:
+ */
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
new file mode 100644 (file)
index 0000000..25b7eb9
--- /dev/null
@@ -0,0 +1,4060 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_generators.h"
+#include "zend_inference.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_worklist.h"
+
+//#define LOG_SSA_RANGE
+//#define LOG_NEG_RANGE
+#define SYM_RANGE
+#define NEG_RANGE
+#define RANGE_WARMAP_PASSES 16
+
+#define CHECK_SCC_VAR(var2) \
+       do { \
+               if (!ssa->vars[var2].no_val) { \
+                       if (dfs[var2] < 0) { \
+                               zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \
+                       } \
+                       if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \
+                               root[var] = root[var2]; \
+                       } \
+               } \
+       } while (0)
+
+#define CHECK_SCC_ENTRY(var2) \
+       do { \
+               if (ssa->vars[var2].scc != ssa->vars[var].scc) { \
+                       ssa->vars[var2].scc_entry = 1; \
+               } \
+       } while (0)
+
+#define ADD_SCC_VAR(_var) \
+       do { \
+               if (ssa->vars[_var].scc == scc) { \
+                       zend_bitset_incl(worklist, _var); \
+               } \
+       } while (0)
+
+#define ADD_SCC_VAR_1(_var) \
+       do { \
+               if (ssa->vars[_var].scc == scc && \
+                   !zend_bitset_in(visited, _var)) { \
+                       zend_bitset_incl(worklist, _var); \
+               } \
+       } while (0)
+
+#define FOR_EACH_DEFINED_VAR(line, MACRO) \
+       do { \
+               if (ssa->ops[line].op1_def >= 0) { \
+                       MACRO(ssa->ops[line].op1_def); \
+               } \
+               if (ssa->ops[line].op2_def >= 0) { \
+                       MACRO(ssa->ops[line].op2_def); \
+               } \
+               if (ssa->ops[line].result_def >= 0) { \
+                       MACRO(ssa->ops[line].result_def); \
+               } \
+               if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \
+                       if (ssa->ops[line-1].op1_def >= 0) { \
+                               MACRO(ssa->ops[line-1].op1_def); \
+                       } \
+                       if (ssa->ops[line-1].op2_def >= 0) { \
+                               MACRO(ssa->ops[line-1].op2_def); \
+                       } \
+                       if (ssa->ops[line-1].result_def >= 0) { \
+                               MACRO(ssa->ops[line-1].result_def); \
+                       } \
+               } else if (line+1 < op_array->last && \
+                          op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \
+                       if (ssa->ops[line+1].op1_def >= 0) { \
+                               MACRO(ssa->ops[line+1].op1_def); \
+                       } \
+                       if (ssa->ops[line+1].op2_def >= 0) { \
+                               MACRO(ssa->ops[line+1].op2_def); \
+                       } \
+                       if (ssa->ops[line+1].result_def >= 0) { \
+                               MACRO(ssa->ops[line+1].result_def); \
+                       } \
+               } \
+       } while (0)
+
+
+#define FOR_EACH_VAR_USAGE(_var, MACRO) \
+       do { \
+               zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \
+               int use = ssa->vars[_var].use_chain; \
+               while (use >= 0) { \
+                       FOR_EACH_DEFINED_VAR(use, MACRO); \
+                       use = zend_ssa_next_use(ssa->ops, _var, use); \
+               } \
+               p = ssa->vars[_var].phi_use_chain; \
+               while (p) { \
+                       MACRO(p->ssa_var); \
+                       p = zend_ssa_next_use_phi(ssa, _var, p); \
+               } \
+       } while (0)
+
+static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */
+{
+#ifdef SYM_RANGE
+       zend_ssa_phi *p;
+#endif
+
+       dfs[var] = *index;
+       (*index)++;
+       root[var] = var;
+
+       FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR);
+
+#ifdef SYM_RANGE
+       /* Process symbolic control-flow constraints */
+       p = ssa->vars[var].sym_use_chain;
+       while (p) {
+               CHECK_SCC_VAR(p->ssa_var);
+               p = p->sym_use_chain;
+       }
+#endif
+
+       if (root[var] == var) {
+               ssa->vars[var].scc = ssa->sccs;
+               while (stack->len > 0) {
+                       int var2 = zend_worklist_stack_peek(stack);
+                       if (dfs[var2] <= dfs[var]) {
+                               break;
+                       }
+                       zend_worklist_stack_pop(stack);
+                       ssa->vars[var2].scc = ssa->sccs;
+               }
+               ssa->sccs++;
+       } else {
+               zend_worklist_stack_push(stack, var);
+       }
+}
+/* }}} */
+
+int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+       int index = 0, *dfs, *root;
+       zend_worklist_stack stack;
+       int j;
+
+       dfs = alloca(sizeof(int) * ssa->vars_count);
+       memset(dfs, -1, sizeof(int) * ssa->vars_count);
+       root = alloca(sizeof(int) * ssa->vars_count);
+       ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count);
+
+       /* Find SCCs */
+       for (j = 0; j < ssa->vars_count; j++) {
+               if (!ssa->vars[j].no_val && dfs[j] < 0) {
+                       zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack);
+               }
+       }
+
+       /* Revert SCC order */
+       for (j = 0; j < ssa->vars_count; j++) {
+               if (ssa->vars[j].scc >= 0) {
+                       ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1);
+               }
+       }
+
+       for (j = 0; j < ssa->vars_count; j++) {
+               if (ssa->vars[j].scc >= 0) {
+                       int var = j;
+                       if (root[j] == j) {
+                               ssa->vars[j].scc_entry = 1;
+                       }
+                       FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY);
+               }
+       }
+
+       return SUCCESS;
+}
+/* }}} */
+
+int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+       zend_ssa_var *ssa_vars = ssa->vars;
+       zend_ssa_op *ssa_ops = ssa->ops;
+       int ssa_vars_count = ssa->vars_count;
+       zend_bitset worklist;
+       int i, j, use;
+       zend_ssa_phi *p;
+
+       if (!op_array->function_name || !ssa->vars || !ssa->ops)
+               return SUCCESS;
+
+       worklist = alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+       memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+       for (i = 0; i < ssa_vars_count; i++) {
+               ssa_vars[i].no_val = 1; /* mark as unused */
+               use = ssa->vars[i].use_chain;
+               while (use >= 0) {
+                       if (op_array->opcodes[use].opcode != ZEND_ASSIGN ||
+                               ssa->ops[use].op1_use != i ||
+                               ssa->ops[use].op2_use == i) {
+                               ssa_vars[i].no_val = 0; /* used directly */
+                               zend_bitset_incl(worklist, i);
+                       }
+                       use = zend_ssa_next_use(ssa_ops, i, use);
+               }
+       }
+
+       while (!zend_bitset_empty(worklist, zend_bitset_len(ssa_vars_count))) {
+               i = zend_bitset_first(worklist, zend_bitset_len(ssa_vars_count));
+               zend_bitset_excl(worklist, i);
+               if (ssa_vars[i].definition_phi) {
+                       /* mark all possible sources as used */
+                       p = ssa_vars[i].definition_phi;
+                       if (p->pi >= 0) {
+                               if (ssa_vars[p->sources[0]].no_val) {
+                                       ssa_vars[p->sources[0]].no_val = 0; /* used indirectly */
+                                       zend_bitset_incl(worklist, p->sources[0]);
+                               }
+                       } else {
+                               for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+                                       if (p->sources[j] >= 0 && ssa->vars[p->sources[j]].no_val) {
+                                               ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */
+                                               zend_bitset_incl(worklist, p->sources[j]);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return SUCCESS;
+}
+/* }}} */
+
+/* From "Hacker's Delight" */
+unsigned long minOR(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+       unsigned long m, temp;
+
+       m = 1L << (sizeof(unsigned long) * 8 - 1);
+       while (m != 0) {
+               if (~a & c & m) {
+                       temp = (a | m) & -m;
+                       if (temp <= b) {
+                               a = temp;
+                               break;
+                       }
+               } else if (a & ~c & m) {
+                       temp = (c | m) & -m;
+                       if (temp <= d) {
+                               c = temp;
+                               break;
+                       }
+               }
+               m = m >> 1;
+       }
+       return a | c;
+}
+
+unsigned long maxOR(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+       unsigned long m, temp;
+
+       m = 1L << (sizeof(unsigned long) * 8 - 1);
+       while (m != 0) {
+               if (b & d & m) {
+                       temp = (b - m) | (m - 1);
+                       if (temp >= a) {
+                               b = temp;
+                               break;
+                       }
+                       temp = (d - m) | (m - 1);
+                       if (temp >= c) {
+                               d = temp;
+                               break;
+                       }
+               }
+               m = m >> 1;
+       }
+       return b | d;
+}
+
+unsigned long minAND(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+       unsigned long m, temp;
+
+       m = 1L << (sizeof(unsigned long) * 8 - 1);
+       while (m != 0) {
+               if (~a & ~c & m) {
+                       temp = (a | m) & -m;
+                       if (temp <= b) {
+                               a = temp;
+                               break;
+                       }
+                       temp = (c | m) & -m;
+                       if (temp <= d) {
+                               c = temp;
+                               break;
+                       }
+               }
+               m = m >> 1;
+       }
+       return a & c;
+}
+
+unsigned long maxAND(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+       unsigned long m, temp;
+
+       m = 1L << (sizeof(unsigned long) * 8 - 1);
+       while (m != 0) {
+               if (b & ~d & m) {
+                       temp = (b | ~m) | (m - 1);
+                       if (temp >= a) {
+                               b = temp;
+                               break;
+                       }
+               } else if (~b & d & m) {
+                       temp = (d | ~m) | (m - 1);
+                       if (temp >= c) {
+                               d = temp;
+                               break;
+                       }
+               }
+               m = m >> 1;
+       }
+       return b & d;
+}
+
+unsigned long minXOR(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+       return minAND(a, b, ~d, ~c) | minAND(~b, ~a, c, d);
+}
+
+unsigned long maxXOR(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+       return maxOR(0, maxAND(a, b, ~d, ~c), 0, maxAND(~b, ~a, c, d));
+}
+
+/* Based on "Hacker's Delight" */
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+3: + + - - 0 0 1 1 => 1 1 - min/max
+8: - + + + 1 0 0 0 => 1 0 ? min(a,-1,b,d)/max(0,b,c,d)
+a: - + - + 1 0 1 0 => 1 0 ? MIN(a,c)/max(0,b,0,d)
+b: - + - - 1 0 1 1 => 1 1 - c/-1
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 1 - a/-1
+f  - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_or(long a, long b, long c, long d, zend_ssa_range *tmp)
+{
+       int x = ((a < 0) ? 8 : 0) |
+               ((b < 0) ? 4 : 0) |
+               ((c < 0) ? 2 : 0) |
+               ((d < 0) ? 2 : 0);
+       switch (x) {
+               case 0x0:
+               case 0x3:
+               case 0xc:
+               case 0xf:
+                       tmp->min = minOR(a, b, c, d);
+                       tmp->max = maxOR(a, b, c, d);
+                       break;
+               case 0x2:
+                       tmp->min = minOR(a, b, c, -1);
+                       tmp->max = maxOR(a, b, 0, d);
+                       break;
+               case 0x8:
+                       tmp->min = minOR(a, -1, c, d);
+                       tmp->max = maxOR(0, b, c, d);
+                       break;
+               case 0xa:
+                       tmp->min = MIN(a, c);
+                       tmp->max = maxOR(0, b, 0, d);
+                       break;
+               case 0xb:
+                       tmp->min = c;
+                       tmp->max = -1;
+                       break;
+               case 0xe:
+                       tmp->min = a;
+                       tmp->max = -1;
+                       break;
+       }
+}
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 0 0 + 0/b
+3: + + - - 0 0 1 1 => 0 0 + min/max
+8: - + + + 1 0 0 0 => 0 0 + 0/d
+a: - + - + 1 0 1 0 => 1 0 ? min(a,-1,c,-1)/NAX(b,d)
+b: - + - - 1 0 1 1 => 1 0 ? min(a,-1,c,d)/max(0,b,c,d)
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+f  - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_and(long a, long b, long c, long d, zend_ssa_range *tmp)
+{
+       int x = ((a < 0) ? 8 : 0) |
+               ((b < 0) ? 4 : 0) |
+               ((c < 0) ? 2 : 0) |
+               ((d < 0) ? 2 : 0);
+       switch (x) {
+               case 0x0:
+               case 0x3:
+               case 0xc:
+               case 0xf:
+                       tmp->min = minAND(a, b, c, d);
+                       tmp->max = maxAND(a, b, c, d);
+                       break;
+               case 0x2:
+                       tmp->min = 0;
+                       tmp->max = b;
+                       break;
+               case 0x8:
+                       tmp->min = 0;
+                       tmp->max = d;
+                       break;
+               case 0xa:
+                       tmp->min = minAND(a, -1, c, -1);
+                       tmp->max = MAX(b, d);
+                       break;
+               case 0xb:
+                       tmp->min = minAND(a, -1, c, d);
+                       tmp->max = maxAND(0, b, c, d);
+                       break;
+               case 0xe:
+                       tmp->min = minAND(a, b, c, -1);
+                       tmp->max = maxAND(a, b, 0, d);
+                       break;
+       }
+}
+
+int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp)
+{
+       uint32_t line;
+       zend_op *opline;
+       long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4;
+
+       if (ssa->vars[var].definition_phi) {
+               zend_ssa_phi *p = ssa->vars[var].definition_phi;
+               int i;
+
+               tmp->underflow = 0;
+               tmp->min = LONG_MAX;
+               tmp->max = LONG_MIN;
+               tmp->overflow = 0;
+               if (p->pi >= 0) {
+                       if (p->constraint.negative) {
+                               if (ssa->var_info[p->sources[0]].has_range) {
+                                       tmp->underflow = ssa->var_info[p->sources[0]].range.underflow;
+                                       tmp->min = ssa->var_info[p->sources[0]].range.min;
+                                       tmp->max = ssa->var_info[p->sources[0]].range.max;
+                                       tmp->overflow = ssa->var_info[p->sources[0]].range.overflow;
+                               } else if (narrowing) {
+                                       tmp->underflow = 1;
+                                       tmp->min = LONG_MIN;
+                                       tmp->max = LONG_MAX;
+                                       tmp->overflow = 1;
+                               }
+#ifdef NEG_RANGE
+                               if (p->constraint.min_ssa_var < 0 &&
+                                   p->constraint.min_ssa_var < 0 &&
+                                   ssa->var_info[p->ssa_var].has_range) {
+#ifdef LOG_NEG_RANGE
+                                       fprintf(stderr, "%s() #%d [%ld..%ld] -> [%ld..%ld]?\n",
+                                               op_array->function_name,
+                                               p->ssa_var,
+                                               ssa->var_info[p->ssa_var].range.min,
+                                               ssa->var_info[p->ssa_var].range.max,
+                                               tmp->min,
+                                               tmp->max);
+#endif
+                                       if (p->constraint.negative == NEG_USE_LT &&
+                                           tmp->max >= p->constraint.range.min) {
+                                               tmp->overflow = 0;
+                                               tmp->max = p->constraint.range.min - 1;
+#ifdef LOG_NEG_RANGE
+                                               fprintf(stderr, "  => [%ld..%ld]\n",
+                                                       tmp->min,
+                                                       tmp->max);
+#endif
+                                       } else if (p->constraint.negative == NEG_USE_GT &&
+                                                  tmp->min <= p->constraint.range.max) {
+                                               tmp->underflow = 0;
+                                               tmp->min = p->constraint.range.max + 1;
+#ifdef LOG_NEG_RANGE
+                                               fprintf(stderr, "  => [%ld..%ld]\n",
+                                                       tmp->min,
+                                                       tmp->max);
+#endif
+                                       }
+                               }
+#endif
+                       } else if (ssa->var_info[p->sources[0]].has_range) {
+                               /* intersection */
+                               tmp->underflow = ssa->var_info[p->sources[0]].range.underflow;
+                               tmp->min = ssa->var_info[p->sources[0]].range.min;
+                               tmp->max = ssa->var_info[p->sources[0]].range.max;
+                               tmp->overflow = ssa->var_info[p->sources[0]].range.overflow;
+                               if (p->constraint.min_ssa_var < 0) {
+                                       tmp->underflow = p->constraint.range.underflow && tmp->underflow;
+                                       tmp->min = MAX(p->constraint.range.min, tmp->min);
+#ifdef SYM_RANGE
+                               } else if (narrowing && ssa->var_info[p->constraint.min_ssa_var].has_range) {
+                                       tmp->underflow = ssa->var_info[p->constraint.min_ssa_var].range.underflow && tmp->underflow;
+                                       tmp->min = MAX(ssa->var_info[p->constraint.min_ssa_var].range.min + p->constraint.range.min, tmp->min);
+#endif
+                               }
+                               if (p->constraint.max_ssa_var < 0) {
+                                       tmp->max = MIN(p->constraint.range.max, tmp->max);
+                                       tmp->overflow = p->constraint.range.overflow && tmp->overflow;
+#ifdef SYM_RANGE
+                               } else if (narrowing && ssa->var_info[p->constraint.max_ssa_var].has_range) {
+                                       tmp->max = MIN(ssa->var_info[p->constraint.max_ssa_var].range.max + p->constraint.range.max, tmp->max);
+                                       tmp->overflow = ssa->var_info[p->constraint.max_ssa_var].range.overflow && tmp->overflow;
+#endif
+                               }
+                       } else if (narrowing) {
+                               if (p->constraint.min_ssa_var < 0) {
+                                       tmp->underflow = p->constraint.range.underflow;
+                                       tmp->min = p->constraint.range.min;
+#ifdef SYM_RANGE
+                               } else if (narrowing && ssa->var_info[p->constraint.min_ssa_var].has_range) {
+                                       tmp->underflow = ssa->var_info[p->constraint.min_ssa_var].range.underflow;
+                                       tmp->min = ssa->var_info[p->constraint.min_ssa_var].range.min + p->constraint.range.min;
+#endif
+                               } else {
+                                       tmp->underflow = 1;
+                                       tmp->min = LONG_MIN;
+                               }
+                               if (p->constraint.max_ssa_var < 0) {
+                                       tmp->max = p->constraint.range.max;
+                                       tmp->overflow = p->constraint.range.overflow;
+#ifdef SYM_RANGE
+                               } else if (narrowing && ssa->var_info[p->constraint.max_ssa_var].has_range) {
+                                       tmp->max = ssa->var_info[p->constraint.max_ssa_var].range.max + p->constraint.range.max;
+                                       tmp->overflow = ssa->var_info[p->constraint.max_ssa_var].range.overflow;
+#endif
+                               } else {
+                                       tmp->max = LONG_MAX;
+                                       tmp->overflow = 1;
+                               }
+                       }
+               } else {
+                       for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
+                               if (p->sources[i] >= 0 && ssa->var_info[p->sources[i]].has_range) {
+                                       /* union */
+                                       tmp->underflow |= ssa->var_info[p->sources[i]].range.underflow;
+                                       tmp->min = MIN(tmp->min, ssa->var_info[p->sources[i]].range.min);
+                                       tmp->max = MAX(tmp->max, ssa->var_info[p->sources[i]].range.max);
+                                       tmp->overflow |= ssa->var_info[p->sources[i]].range.overflow;
+                               } else if (narrowing) {
+                                       tmp->underflow = 1;
+                                       tmp->min = LONG_MIN;
+                                       tmp->max = LONG_MAX;
+                                       tmp->overflow = 1;
+                               }
+                       }
+               }
+               return (tmp->min <= tmp->max);
+       } else if (ssa->vars[var].definition < 0) {
+               if (var < op_array->last_var &&
+                   var != EX_VAR_TO_NUM(op_array->this_var) &&
+                   op_array->function_name) {
+
+                       tmp->min = 0;
+                       tmp->max = 0;
+                       tmp->underflow = 0;
+                       tmp->overflow = 0;
+                       return 1;
+               }
+               return 0;
+       }
+       line = ssa->vars[var].definition;
+       opline = op_array->opcodes + line;
+
+       tmp->underflow = 0;
+       tmp->overflow = 0;
+       switch (opline->opcode) {
+               case ZEND_ADD:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+                                       tmp->min = op1_min + op2_min;
+                                       tmp->max = op1_max + op2_max;
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
+                                               tmp->underflow = 1;
+                                               tmp->min = LONG_MIN;
+                                       }
+                                       if (OP1_RANGE_OVERFLOW() ||
+                                           OP2_RANGE_OVERFLOW() ||
+                                               (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
+                                               tmp->overflow = 1;
+                                               tmp->max = LONG_MAX;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_SUB:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+                                       tmp->min = op1_min - op2_max;
+                                       tmp->max = op1_max - op2_min;
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_OVERFLOW() ||
+                                           (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
+                                               tmp->underflow = 1;
+                                               tmp->min = LONG_MIN;
+                                       }
+                                       if (OP1_RANGE_OVERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                               (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
+                                               tmp->overflow = 1;
+                                               tmp->max = LONG_MAX;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_MUL:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+                                       t1 = op1_min * op2_min;
+                                       t2 = op1_min * op2_max;
+                                       t3 = op1_max * op2_min;
+                                       t4 = op1_max * op2_max;
+                                       // FIXME: more careful overflow checks?
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW()  ||
+                                           OP2_RANGE_OVERFLOW()  ||
+                                           (double)t1 != (double)op1_min * (double)op2_min ||
+                                           (double)t2 != (double)op1_min * (double)op2_max ||
+                                           (double)t3 != (double)op1_max * (double)op2_min ||
+                                           (double)t4 != (double)op1_max * (double)op2_max) {
+                                               tmp->underflow = 1;
+                                               tmp->overflow = 1;
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                               tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_DIV:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+                                       if (op2_min <= 0 && op2_max >= 0) {
+                                               break;
+                                       }
+                                       t1 = op1_min / op2_min;
+                                       t2 = op1_min / op2_max;
+                                       t3 = op1_max / op2_min;
+                                       t4 = op1_max / op2_max;
+                                       // FIXME: more careful overflow checks?
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW()  ||
+                                           OP2_RANGE_OVERFLOW()  ||
+                                           t1 != (long)((double)op1_min / (double)op2_min) ||
+                                           t2 != (long)((double)op1_min / (double)op2_max) ||
+                                           t3 != (long)((double)op1_max / (double)op2_min) ||
+                                           t4 != (long)((double)op1_max / (double)op2_max)) {
+                                               tmp->underflow = 1;
+                                               tmp->overflow = 1;
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                               tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_MOD:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW()  ||
+                                           OP2_RANGE_OVERFLOW()) {
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               if (op2_min == 0 || op2_max == 0) {
+                                                       /* avoid division by zero */
+                                                       break;
+                                               }
+                                               t1 = (op2_min == -1) ? 0 : (op1_min % op2_min);
+                                               t2 = (op2_max == -1) ? 0 : (op1_min % op2_max);
+                                               t3 = (op2_min == -1) ? 0 : (op1_max % op2_min);
+                                               t4 = (op2_max == -1) ? 0 : (op1_max % op2_max);
+                                               tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                               tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_SL:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW() ||
+                                           OP2_RANGE_OVERFLOW()) {
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               t1 = op1_min << op2_min;
+                                               t2 = op1_min << op2_max;
+                                               t3 = op1_max << op2_min;
+                                               t4 = op1_max << op2_max;
+                                               tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                               tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_SR:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW() ||
+                                           OP2_RANGE_OVERFLOW()) {
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               t1 = op1_min >> op2_min;
+                                               t2 = op1_min >> op2_max;
+                                               t3 = op1_max >> op2_min;
+                                               t4 = op1_max >> op2_max;
+                                               tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                               tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_BW_OR:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW() ||
+                                           OP2_RANGE_OVERFLOW()) {
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_BW_AND:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP2_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW() ||
+                                           OP2_RANGE_OVERFLOW()) {
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+//             case ZEND_BW_XOR:
+               case ZEND_BW_NOT:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       if (OP1_RANGE_UNDERFLOW() ||
+                                           OP1_RANGE_OVERFLOW()) {
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                       } else {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               tmp->min = ~op1_max;
+                                               tmp->max = ~op1_min;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_CAST:
+                       if (ssa->ops[line].result_def == var) {
+                               if (opline->extended_value == IS_NULL) {
+                                       tmp->min = 0;
+                                       tmp->max = 0;
+                                       return 1;
+                               } else if (opline->extended_value == _IS_BOOL) {
+                                       if (OP1_HAS_RANGE()) {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               tmp->min = (op1_min > 0 || op1_max < 0);
+                                               tmp->max = (op1_min != 0 || op1_max != 0);
+                                               return 1;
+                                       } else {
+                                               tmp->min = 0;
+                                               tmp->max = 1;
+                                               return 1;
+                                       }
+                               } else if (opline->extended_value == IS_LONG) {
+                                       if (OP1_HAS_RANGE()) {
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               return 1;
+                                       } else {
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_BOOL:
+               case ZEND_JMPZ_EX:
+               case ZEND_JMPNZ_EX:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       tmp->min = (op1_min > 0 || op1_max < 0);
+                                       tmp->max = (op1_min != 0 || op1_max != 0);
+                                       return 1;
+                               } else {
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_BOOL_NOT:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       tmp->min = (op1_min == 0 && op1_max == 0);
+                                       tmp->max = (op1_min <= 0 && op1_max >= 0);
+                                       return 1;
+                               } else {
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_BOOL_XOR:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+                                       op1_min = (op1_min > 0 || op1_max < 0);
+                                       op1_max = (op1_min != 0 || op1_max != 0);
+                                       op2_min = (op2_min > 0 || op2_max < 0);
+                                       op2_max = (op2_min != 0 || op2_max != 0);
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       if (op1_min == op1_max && op2_min == op2_max) {
+                                               if (op1_min == op2_min) {
+                                                       tmp->max = 0;
+                                               } else {
+                                                       tmp->min = 1;
+                                               }
+                                       }
+                                       return 1;
+                               } else {
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_IS_IDENTICAL:
+               case ZEND_IS_EQUAL:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+
+                                       tmp->min = (op1_min == op1_max &&
+                                                  op2_min == op2_min &&
+                                                  op1_min == op2_max);
+                                       tmp->max = (op1_min <= op2_max && op1_max >= op2_min);
+                                       return 1;
+                               } else {
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_IS_NOT_IDENTICAL:
+               case ZEND_IS_NOT_EQUAL:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+
+                                       tmp->min = (op1_min > op2_max || op1_max < op2_min);
+                                       tmp->max = (op1_min != op1_max ||
+                                                  op2_min != op2_min ||
+                                                  op1_min != op2_max);
+                                       return 1;
+                               } else {
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_IS_SMALLER:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+
+                                       tmp->min = op1_max < op2_min;
+                                       tmp->max = op1_min < op2_max;
+                                       return 1;
+                               } else {
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_IS_SMALLER_OR_EQUAL:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                       op1_min = OP1_MIN_RANGE();
+                                       op2_min = OP2_MIN_RANGE();
+                                       op1_max = OP1_MAX_RANGE();
+                                       op2_max = OP2_MAX_RANGE();
+
+                                       tmp->min = op1_max <= op2_min;
+                                       tmp->max = op1_min <= op2_max;
+                                       return 1;
+                               } else {
+                                       tmp->min = 0;
+                                       tmp->max = 1;
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_QM_ASSIGN:
+               case ZEND_COALESCE:
+                       if (ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       tmp->min = OP1_MIN_RANGE();
+                                       tmp->max = OP1_MAX_RANGE();
+                                       tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                       tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_ASSERT_CHECK:
+                       if (ssa->ops[line].result_def == var) {
+                               tmp->min = 0;
+                               tmp->max = 1;
+                               return 1;
+                       }
+                       break;
+               case ZEND_SEND_VAR:
+                       if (ssa->ops[line].op1_def == var) {
+                               if (ssa->ops[line].op1_def >= 0) {
+                                       if (OP1_HAS_RANGE()) {
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_PRE_INC:
+                       if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       tmp->min = OP1_MIN_RANGE();
+                                       tmp->max = OP1_MAX_RANGE();
+                                       tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                       tmp->overflow = OP1_RANGE_OVERFLOW();
+                                       if (tmp->max < LONG_MAX) {
+                                               tmp->max++;
+                                       } else {
+                                               tmp->overflow = 1;
+                                       }
+                                       if (tmp->min < LONG_MAX && !tmp->underflow) {
+                                               tmp->min++;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_PRE_DEC:
+                       if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       tmp->min = OP1_MIN_RANGE();
+                                       tmp->max = OP1_MAX_RANGE();
+                                       tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                       tmp->overflow = OP1_RANGE_OVERFLOW();
+                                       if (tmp->min > LONG_MIN) {
+                                               tmp->min--;
+                                       } else {
+                                               tmp->underflow = 1;
+                                       }
+                                       if (tmp->max > LONG_MIN && !tmp->overflow) {
+                                               tmp->max--;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_POST_INC:
+                       if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       tmp->min = OP1_MIN_RANGE();
+                                       tmp->max = OP1_MAX_RANGE();
+                                       tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                       tmp->overflow = OP1_RANGE_OVERFLOW();
+                                       if (ssa->ops[line].result_def == var) {
+                                               return 1;
+                                       }
+                                       if (tmp->max < LONG_MAX) {
+                                               tmp->max++;
+                                       } else {
+                                               tmp->overflow = 1;
+                                       }
+                                       if (tmp->min < LONG_MAX && !tmp->underflow) {
+                                               tmp->min++;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_POST_DEC:
+                       if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                               if (OP1_HAS_RANGE()) {
+                                       tmp->min = OP1_MIN_RANGE();
+                                       tmp->max = OP1_MAX_RANGE();
+                                       tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                       tmp->overflow = OP1_RANGE_OVERFLOW();
+                                       if (ssa->ops[line].result_def == var) {
+                                               return 1;
+                                       }
+                                       if (tmp->min > LONG_MIN) {
+                                               tmp->min--;
+                                       } else {
+                                               tmp->underflow = 1;
+                                       }
+                                       if (tmp->max > LONG_MIN && !tmp->overflow) {
+                                               tmp->max--;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_UNSET_DIM:
+               case ZEND_UNSET_OBJ:
+                       if (ssa->ops[line].op1_def == var) {
+                               /* If op1 is scalar, UNSET_DIM and UNSET_OBJ have no effect, so we can keep
+                                * the previous ranges. */
+                               if (OP1_HAS_RANGE()) {
+                                       tmp->min = OP1_MIN_RANGE();
+                                       tmp->max = OP1_MAX_RANGE();
+                                       tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                       tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN:
+                       if (ssa->ops[line].op1_def == var || ssa->ops[line].op2_def == var || ssa->ops[line].result_def == var) {
+                               if (OP2_HAS_RANGE()) {
+                                       tmp->min = OP2_MIN_RANGE();
+                                       tmp->max = OP2_MAX_RANGE();
+                                       tmp->underflow = OP2_RANGE_UNDERFLOW();
+                                       tmp->overflow  = OP2_RANGE_OVERFLOW();
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_DIM:
+               case ZEND_ASSIGN_OBJ:
+                       if (ssa->ops[line+1].op1_def == var) {
+                               if ((opline+1)->opcode == ZEND_OP_DATA) {
+                                       opline++;
+                                       tmp->min = OP1_MIN_RANGE();
+                                       tmp->max = OP1_MAX_RANGE();
+                                       tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                       tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                       return 1;
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_ADD:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               tmp->min = op1_min + op2_min;
+                                               tmp->max = op1_max + op2_max;
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
+                                                       tmp->underflow = 1;
+                                                       tmp->min = LONG_MIN;
+                                               }
+                                               if (OP1_RANGE_OVERFLOW() ||
+                                                   OP2_RANGE_OVERFLOW() ||
+                                                   (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
+                                                       tmp->overflow = 1;
+                                                       tmp->max = LONG_MAX;
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       opline++;
+                                       if (OP1_HAS_RANGE()) {
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_SUB:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               tmp->min = op1_min - op2_max;
+                                               tmp->max = op1_max - op2_min;
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_OVERFLOW()  ||
+                                                   (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
+                                                       tmp->underflow = 1;
+                                                       tmp->min = LONG_MIN;
+                                               }
+                                               if (OP1_RANGE_OVERFLOW()  ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                       (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
+                                                       tmp->overflow = 1;
+                                                       tmp->max = LONG_MAX;
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       opline++;
+                                       if (OP1_HAS_RANGE()) {
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_MUL:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               t1 = op1_min * op2_min;
+                                               t2 = op1_min * op2_max;
+                                               t3 = op1_max * op2_min;
+                                               t4 = op1_max * op2_max;
+                                               // FIXME: more careful overflow checks?
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   OP1_RANGE_OVERFLOW()  ||
+                                                   OP2_RANGE_OVERFLOW()  ||
+                                                   (double)t1 != (double)op1_min * (double)op2_min ||
+                                                   (double)t2 != (double)op1_min * (double)op2_min ||
+                                                       (double)t3 != (double)op1_min * (double)op2_min ||
+                                                       (double)t4 != (double)op1_min * (double)op2_min) {
+                                                       tmp->underflow = 1;
+                                                       tmp->overflow = 1;
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else {
+                                                       tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                                       tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               opline++;
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_DIV:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               op1_min = OP1_MIN_RANGE();
+                                               op2_min = OP2_MIN_RANGE();
+                                               op1_max = OP1_MAX_RANGE();
+                                               op2_max = OP2_MAX_RANGE();
+                                               if (op2_min <= 0 && op2_max >= 0) {
+                                                       break;
+                                               }
+                                               t1 = op1_min / op2_min;
+                                               t2 = op1_min / op2_max;
+                                               t3 = op1_max / op2_min;
+                                               t4 = op1_max / op2_max;
+                                               // FIXME: more careful overflow checks?
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   OP1_RANGE_OVERFLOW()  ||
+                                                   OP2_RANGE_OVERFLOW()  ||
+                                                   t1 != (long)((double)op1_min / (double)op2_min) ||
+                                                   t2 != (long)((double)op1_min / (double)op2_max) ||
+                                                   t3 != (long)((double)op1_max / (double)op2_min) ||
+                                                   t4 != (long)((double)op1_max / (double)op2_max)) {
+                                                       tmp->underflow = 1;
+                                                       tmp->overflow = 1;
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else {
+                                                       tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                                       tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               opline++;
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_MOD:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   OP1_RANGE_OVERFLOW()  ||
+                                                   OP2_RANGE_OVERFLOW()) {
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else {
+                                                       op1_min = OP1_MIN_RANGE();
+                                                       op2_min = OP2_MIN_RANGE();
+                                                       op1_max = OP1_MAX_RANGE();
+                                                       op2_max = OP2_MAX_RANGE();
+                                                       if (op2_min == 0 || op2_max == 0) {
+                                                               /* avoid division by zero */
+                                                               break;
+                                                       }
+                                                       t1 = op1_min % op2_min;
+                                                       t2 = op1_min % op2_max;
+                                                       t3 = op1_max % op2_min;
+                                                       t4 = op1_max % op2_max;
+                                                       tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                                       tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               opline++;
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_SL:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   OP1_RANGE_OVERFLOW() ||
+                                                   OP2_RANGE_OVERFLOW()) {
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else {
+                                                       op1_min = OP1_MIN_RANGE();
+                                                       op2_min = OP2_MIN_RANGE();
+                                                       op1_max = OP1_MAX_RANGE();
+                                                       op2_max = OP2_MAX_RANGE();
+                                                       t1 = op1_min << op2_min;
+                                                       t2 = op1_min << op2_max;
+                                                       t3 = op1_max << op2_min;
+                                                       t4 = op1_max << op2_max;
+                                                       tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                                       tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               opline++;
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_SR:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   OP1_RANGE_OVERFLOW() ||
+                                                   OP2_RANGE_OVERFLOW()) {
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else {
+                                                       op1_min = OP1_MIN_RANGE();
+                                                       op2_min = OP2_MIN_RANGE();
+                                                       op1_max = OP1_MAX_RANGE();
+                                                       op2_max = OP2_MAX_RANGE();
+                                                       t1 = op1_min >> op2_min;
+                                                       t2 = op1_min >> op2_max;
+                                                       t3 = op1_max >> op2_min;
+                                                       t4 = op1_max >> op2_max;
+                                                       tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+                                                       tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               opline++;
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_BW_OR:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   OP1_RANGE_OVERFLOW() ||
+                                                   OP2_RANGE_OVERFLOW()) {
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else {
+                                                       op1_min = OP1_MIN_RANGE();
+                                                       op2_min = OP2_MIN_RANGE();
+                                                       op1_max = OP1_MAX_RANGE();
+                                                       op2_max = OP2_MAX_RANGE();
+                                                       zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               opline++;
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_BW_AND:
+                       if (opline->extended_value == 0) {
+                               if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+                                       if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+                                               if (OP1_RANGE_UNDERFLOW() ||
+                                                   OP2_RANGE_UNDERFLOW() ||
+                                                   OP1_RANGE_OVERFLOW() ||
+                                                   OP2_RANGE_OVERFLOW()) {
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else {
+                                                       op1_min = OP1_MIN_RANGE();
+                                                       op2_min = OP2_MIN_RANGE();
+                                                       op1_max = OP1_MAX_RANGE();
+                                                       op2_max = OP2_MAX_RANGE();
+                                                       zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
+                                               }
+                                               return 1;
+                                       }
+                               }
+                       } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[line+1].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               opline++;
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+//             case ZEND_ASSIGN_BW_XOR:
+//             case ZEND_ASSIGN_CONCAT:
+               case ZEND_OP_DATA:
+                       if ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
+                           (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
+                           (opline-1)->opcode == ZEND_ASSIGN_ADD ||
+                           (opline-1)->opcode == ZEND_ASSIGN_SUB ||
+                           (opline-1)->opcode == ZEND_ASSIGN_MUL) {
+                               if (ssa->ops[line].op1_def == var) {
+                                       if (OP1_HAS_RANGE()) {
+                                               tmp->min = OP1_MIN_RANGE();
+                                               tmp->max = OP1_MAX_RANGE();
+                                               tmp->underflow = OP1_RANGE_UNDERFLOW();
+                                               tmp->overflow  = OP1_RANGE_OVERFLOW();
+                                               return 1;
+                                       }
+                               }
+                               break;
+                       }
+                       break;
+               case ZEND_RECV:
+               case ZEND_RECV_INIT:
+                       if (ssa->ops[line].result_def == var) {
+                               zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+
+                               if (func_info &&
+                                   (int)opline->op1.num-1 < func_info->num_args &&
+                                   func_info->arg_info[opline->op1.num-1].info.has_range) {
+                                       *tmp = func_info->arg_info[opline->op1.num-1].info.range;
+                                       return 1;
+                               } else if (op_array->arg_info &&
+                                   opline->op1.num <= op_array->num_args) {
+                                       if (op_array->arg_info[opline->op1.num-1].type_hint == IS_LONG) {
+                                               tmp->underflow = 0;
+                                               tmp->min = LONG_MIN;
+                                               tmp->max = LONG_MAX;
+                                               tmp->overflow = 0;
+                                               return 1;
+                                       } else if (op_array->arg_info[opline->op1.num-1].type_hint == _IS_BOOL) {
+                                               tmp->underflow = 0;
+                                               tmp->min = 0;
+                                               tmp->max = 1;
+                                               tmp->overflow = 0;
+                                               return 1;
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_DO_FCALL:
+               case ZEND_DO_ICALL:
+               case ZEND_DO_UCALL:
+               case ZEND_DO_FCALL_BY_NAME:
+                       if (ssa->ops[line].result_def == var && ZEND_FUNC_INFO(op_array)) {
+                               zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+                               zend_call_info *call_info = func_info->callee_info;
+
+                               while (call_info && call_info->caller_call_opline != opline) {
+                                       call_info = call_info->next_callee;
+                               }
+                               if (call_info) {
+                                       if (call_info->callee_func->type == ZEND_USER_FUNCTION) {
+                                               func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array);
+                                               if (func_info && func_info->return_info.has_range) {
+                                                       *tmp = func_info->return_info.range;
+                                                       return 1;
+                                               }
+                                       }
+//TODO: we can't use type inference for internal functions at this point ???
+#if 0
+                                       uint32_t type;
+
+                                       type = zend_get_func_info(call_info, ssa);
+                                       if (!(type & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)))) {
+                                               tmp->underflow = 0;
+                                               tmp->min = 0;
+                                               tmp->max = 0;
+                                               tmp->overflow = 0;
+                                               if (type & MAY_BE_LONG) {
+                                                       tmp->min = LONG_MIN;
+                                                       tmp->max = LONG_MAX;
+                                               } else if (type & MAY_BE_TRUE) {
+                                                       if (!(type & (MAY_BE_NULL|MAY_BE_FALSE))) {
+                                                               tmp->min = 1;
+                                                       }
+                                                       tmp->max = 1;
+                                               }
+                                               return 1;
+                                       }
+#endif
+                               }
+                       }
+                       break;
+               // FIXME: support for more opcodes
+               default:
+                       break;
+       }
+       return 0;
+}
+
+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)
+{
+       if (underflow) {
+               min = LONG_MIN;
+       }
+       if (overflow) {
+               max = LONG_MAX;
+       }
+       ssa->var_info[var].has_range = 1;
+       ssa->var_info[var].range.underflow = underflow;
+       ssa->var_info[var].range.min = min;
+       ssa->var_info[var].range.max = max;
+       ssa->var_info[var].range.overflow = overflow;
+#ifdef LOG_SSA_RANGE
+       fprintf(stderr, "  change range (init      SCC %2d) %2d [%s%ld..%ld%s]\n", ssa->vars[var].scc, var, (underflow?"-- ":""), min, max, (overflow?" ++":""));
+#endif
+}
+
+int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+       if (!var_info->has_range) {
+               var_info->has_range = 1;
+       } else {
+               if (r->underflow ||
+                   var_info->range.underflow ||
+                   r->min < var_info->range.min) {
+                       r->underflow = 1;
+                       r->min = LONG_MIN;
+               }
+               if (r->overflow ||
+                   var_info->range.overflow ||
+                   r->max > var_info->range.max) {
+                       r->overflow = 1;
+                       r->max = LONG_MAX;
+               }
+               if (var_info->range.min == r->min &&
+                   var_info->range.max == r->max &&
+                   var_info->range.underflow == r->underflow &&
+                   var_info->range.overflow == r->overflow) {
+                       return 0;
+               }
+       }
+       var_info->range = *r;
+       return 1;
+}
+
+static int zend_ssa_range_widening(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+       zend_ssa_range tmp;
+
+       if (zend_inference_calc_range(op_array, ssa, var, 1, 0, &tmp)) {
+               if (zend_inference_widening_meet(&ssa->var_info[var], &tmp)) {
+#ifdef LOG_SSA_RANGE
+                       fprintf(stderr, "  change range (widening  SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+#endif
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+       if (!var_info->has_range) {
+               var_info->has_range = 1;
+       } else {
+               if (!r->underflow &&
+                   !var_info->range.underflow &&
+                   var_info->range.min < r->min) {
+                       r->min = var_info->range.min;
+               }
+               if (!r->overflow &&
+                   !var_info->range.overflow &&
+                   var_info->range.max > r->max) {
+                       r->max = var_info->range.max;
+               }
+               if (r->underflow) {
+                       r->min = LONG_MIN;
+               }
+               if (r->overflow) {
+                       r->max = LONG_MAX;
+               }
+               if (var_info->range.min == r->min &&
+                   var_info->range.max == r->max &&
+                   var_info->range.underflow == r->underflow &&
+                   var_info->range.overflow == r->overflow) {
+                       return 0;
+               }
+       }
+       var_info->range = *r;
+       return 1;
+}
+
+static int zend_ssa_range_narrowing(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+       zend_ssa_range tmp;
+
+       if (zend_inference_calc_range(op_array, ssa, var, 0, 1, &tmp)) {
+               if (zend_inference_narrowing_meet(&ssa->var_info[var], &tmp)) {
+#ifdef LOG_SSA_RANGE
+                       fprintf(stderr, "  change range (narrowing SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+#endif
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+#ifdef NEG_RANGE
+# define CHECK_INNER_CYCLE(var2) \
+       do { \
+               if (ssa->vars[var2].scc == ssa->vars[var].scc && \
+                   !ssa->vars[var2].scc_entry && \
+                   !zend_bitset_in(visited, var2) && \
+                       zend_check_inner_cycles(op_array, ssa, worklist, visited, var2)) { \
+                       return 1; \
+               } \
+       } while (0)
+
+static int zend_check_inner_cycles(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, zend_bitset visited, int var)
+{
+       if (zend_bitset_in(worklist, var)) {
+               return 1;
+       }
+       zend_bitset_incl(worklist, var);
+       FOR_EACH_VAR_USAGE(var, CHECK_INNER_CYCLE);
+       zend_bitset_incl(visited, var);
+       return 0;
+}
+#endif
+
+static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ssa, int *scc_var, int *next_scc_var, int scc)
+{
+       int worklist_len = zend_bitset_len(ssa->vars_count);
+       zend_bitset worklist = alloca(sizeof(zend_ulong) * worklist_len);
+       zend_bitset visited = alloca(sizeof(zend_ulong) * worklist_len);
+       int j, n;
+       zend_ssa_range tmp;
+#ifdef NEG_RANGE
+       int has_inner_cycles = 0;
+
+       memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+       memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+       j= scc_var[scc];
+       while (j >= 0) {
+               if (!zend_bitset_in(visited, j) &&
+                   zend_check_inner_cycles(op_array, ssa, worklist, visited, j)) {
+                       has_inner_cycles = 1;
+                       break;
+               }
+               j = next_scc_var[j];
+       }
+#endif
+
+       memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+
+       for (n = 0; n < RANGE_WARMAP_PASSES; n++) {
+               j= scc_var[scc];
+               while (j >= 0) {
+                       if (ssa->vars[j].scc_entry) {
+                               zend_bitset_incl(worklist, j);
+                       }
+                       j = next_scc_var[j];
+               }
+
+               memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+
+               while (!zend_bitset_empty(worklist, worklist_len)) {
+                       j = zend_bitset_first(worklist, worklist_len);
+                       zend_bitset_excl(worklist, j);
+                       if (zend_inference_calc_range(op_array, ssa, j, 0, 0, &tmp)) {
+#ifdef NEG_RANGE
+                               if (!has_inner_cycles &&
+                                   ssa->var_info[j].has_range &&
+                                   ssa->vars[j].definition_phi &&
+                                   ssa->vars[j].definition_phi->pi >= 0 &&
+                                   ssa->vars[j].definition_phi->constraint.negative &&
+                                   ssa->vars[j].definition_phi->constraint.min_ssa_var < 0 &&
+                                   ssa->vars[j].definition_phi->constraint.min_ssa_var < 0) {
+                                       if (tmp.min == ssa->var_info[j].range.min &&
+                                           tmp.max == ssa->var_info[j].range.max) {
+                                               if (ssa->vars[j].definition_phi->constraint.negative == NEG_INIT) {
+#ifdef LOG_NEG_RANGE
+                                                       fprintf(stderr, "#%d INVARIANT\n", j);
+#endif
+                                                       ssa->vars[j].definition_phi->constraint.negative = NEG_INVARIANT;
+                                               }
+                                       } else if (tmp.min == ssa->var_info[j].range.min &&
+                                                  tmp.max == ssa->var_info[j].range.max + 1 &&
+                                                  tmp.max < ssa->vars[j].definition_phi->constraint.range.min) {
+                                               if (ssa->vars[j].definition_phi->constraint.negative == NEG_INIT ||
+                                                   ssa->vars[j].definition_phi->constraint.negative == NEG_INVARIANT) {
+#ifdef LOG_NEG_RANGE
+                                                       fprintf(stderr, "#%d LT\n", j);
+#endif
+                                                       ssa->vars[j].definition_phi->constraint.negative = NEG_USE_LT;
+//???NEG
+                                               } else if (ssa->vars[j].definition_phi->constraint.negative == NEG_USE_GT) {
+#ifdef LOG_NEG_RANGE
+                                                       fprintf(stderr, "#%d UNKNOWN\n", j);
+#endif
+                                                       ssa->vars[j].definition_phi->constraint.negative = NEG_UNKNOWN;
+                                               }
+                                       } else if (tmp.max == ssa->var_info[j].range.max &&
+                                              tmp.min == ssa->var_info[j].range.min - 1 &&
+                                                  tmp.min > ssa->vars[j].definition_phi->constraint.range.max) {
+                                               if (ssa->vars[j].definition_phi->constraint.negative == NEG_INIT ||
+                                                   ssa->vars[j].definition_phi->constraint.negative == NEG_INVARIANT) {
+#ifdef LOG_NEG_RANGE
+                                                       fprintf(stderr, "#%d GT\n", j);
+#endif
+                                                       ssa->vars[j].definition_phi->constraint.negative = NEG_USE_GT;
+//???NEG
+                                               } else if (ssa->vars[j].definition_phi->constraint.negative == NEG_USE_LT) {
+#ifdef LOG_NEG_RANGE
+                                                       fprintf(stderr, "#%d UNKNOWN\n", j);
+#endif
+                                                       ssa->vars[j].definition_phi->constraint.negative = NEG_UNKNOWN;
+                                               }
+                                       } else {
+#ifdef LOG_NEG_RANGE
+                                               fprintf(stderr, "#%d UNKNOWN\n", j);
+#endif
+                                               ssa->vars[j].definition_phi->constraint.negative = NEG_UNKNOWN;
+                                       }
+                               }
+#endif
+                               if (zend_inference_narrowing_meet(&ssa->var_info[j], &tmp)) {
+#ifdef LOG_SSA_RANGE
+                                       fprintf(stderr, "  change range (warmup %d  SCC %2d) %2d [%s%ld..%ld%s]\n", n, scc, j, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+#endif
+                                       zend_bitset_incl(visited, j);
+                                       FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR_1);
+                               }
+                       }
+               }
+       }
+}
+
+static int zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+       int worklist_len = zend_bitset_len(ssa->vars_count);
+       zend_bitset worklist = alloca(sizeof(zend_ulong) * worklist_len);
+       int *next_scc_var = alloca(sizeof(int) * ssa->vars_count);
+       int *scc_var = alloca(sizeof(int) * ssa->sccs);
+       zend_ssa_phi *p;
+       zend_ssa_range tmp;
+       int scc, j;
+
+#ifdef LOG_SSA_RANGE
+       fprintf(stderr, "Range Inference\n");
+#endif
+
+       /* Create linked lists of SSA variables for each SCC */
+       memset(scc_var, -1, sizeof(int) * ssa->sccs);
+       for (j = 0; j < ssa->vars_count; j++) {
+               if (ssa->vars[j].scc >= 0) {
+                       next_scc_var[j] = scc_var[ssa->vars[j].scc];
+                       scc_var[ssa->vars[j].scc] = j;
+               }
+       }
+
+       for (scc = 0; scc < ssa->sccs; scc++) {
+               j = scc_var[scc];
+               if (next_scc_var[j] < 0) {
+                       /* SCC with a single element */
+                       if (zend_inference_calc_range(op_array, ssa, j, 0, 1, &tmp)) {
+                               zend_inference_init_range(op_array, ssa, j, tmp.underflow, tmp.min, tmp.max, tmp.overflow);
+                       } else {
+                               zend_inference_init_range(op_array, ssa, j, 1, LONG_MIN, LONG_MAX, 1);
+                       }
+               } else {
+                       /* Find SCC entry points */
+                       memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+                       do {
+                               if (ssa->vars[j].scc_entry) {
+                                       zend_bitset_incl(worklist, j);
+                               }
+                               j = next_scc_var[j];
+                       } while (j >= 0);
+
+#if RANGE_WARMAP_PASSES > 0
+                       zend_infer_ranges_warmup(op_array, ssa, scc_var, next_scc_var, scc);
+                       j = scc_var[scc];
+                       do {
+                               zend_bitset_incl(worklist, j);
+                               j = next_scc_var[j];
+                       } while (j >= 0);
+#endif
+
+                       /* widening */
+                       while (!zend_bitset_empty(worklist, worklist_len)) {
+                               j = zend_bitset_first(worklist, worklist_len);
+                               zend_bitset_excl(worklist, j);
+                               if (zend_ssa_range_widening(op_array, ssa, j, scc)) {
+                                       FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+                               }
+                       }
+
+                       /* Add all SCC entry variables into worklist for narrowing */
+                       for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) {
+                               if (!ssa->var_info[j].has_range) {
+                                       zend_inference_init_range(op_array, ssa, j, 1, LONG_MIN, LONG_MAX, 1);
+                               }
+                               zend_bitset_incl(worklist, j);
+                       }
+
+                       /* narrowing */
+                       while (!zend_bitset_empty(worklist, worklist_len)) {
+                               j = zend_bitset_first(worklist, worklist_len);
+                               zend_bitset_excl(worklist, j);
+                               if (zend_ssa_range_narrowing(op_array, ssa, j, scc)) {
+                                       FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+#ifdef SYM_RANGE
+                                       /* Process symbolic control-flow constraints */
+                                       p = ssa->vars[j].sym_use_chain;
+                                       while (p) {
+                                               ADD_SCC_VAR(p->ssa_var);
+                                               p = p->sym_use_chain;
+                                       }
+#endif
+                               }
+                       }
+               }
+       }
+
+       return SUCCESS;
+}
+/* }}} */
+
+#define UPDATE_SSA_TYPE(_type, var)                                                                            \
+       do {                                                                                                                            \
+               uint32_t __type = (_type);                                                                              \
+               if (__type & MAY_BE_REF) {                                                                              \
+                       __type |= MAY_BE_RC1 | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; \
+               }                                                                                                                               \
+               if (var >= 0) {                                                                                                 \
+                       if (ssa_var_info[var].type != __type) {                                         \
+                               check_type_narrowing(op_array, ssa, worklist,           \
+                                       var, ssa_var_info[var].type, __type);               \
+                               ssa_var_info[var].type = __type;                                                \
+                               add_usages(op_array, ssa, worklist, var);                                       \
+                       }                                                                                                                       \
+                       /*zend_bitset_excl(worklist, var);*/                                            \
+               }                                                                                                                               \
+       } while (0)
+
+#define UPDATE_SSA_OBJ_TYPE(_ce, _is_instanceof, var)                              \
+       do {                                                                \
+               if (var >= 0) {                                                                                                 \
+                       if (ssa_var_info[var].ce != _ce ||                          \
+                           ssa_var_info[var].is_instanceof != _is_instanceof) {    \
+                               ssa_var_info[var].ce = _ce;                                                     \
+                               ssa_var_info[var].is_instanceof = _is_instanceof;       \
+                               add_usages(op_array, ssa, worklist, var);                                       \
+                       }                                                                                                                       \
+                       /*zend_bitset_excl(worklist, var);*/                                            \
+               }                                                                                                                               \
+       } while (0)
+
+static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+{
+       if (ssa->vars[var].phi_use_chain) {
+               zend_ssa_phi *p = ssa->vars[var].phi_use_chain;
+               do {
+                       zend_bitset_incl(worklist, p->ssa_var);
+                       p = zend_ssa_next_use_phi(ssa, var, p);
+               } while (p);
+       }
+       if (ssa->vars[var].use_chain >= 0) {
+               int use = ssa->vars[var].use_chain;
+               do {
+                       if (ssa->ops[use].result_def >= 0) {
+                               zend_bitset_incl(worklist, ssa->ops[use].result_def);
+                       }
+                       if (ssa->ops[use].op1_def >= 0) {
+                               zend_bitset_incl(worklist, ssa->ops[use].op1_def);
+                       }
+                       if (ssa->ops[use].op2_def >= 0) {
+                               zend_bitset_incl(worklist, ssa->ops[use].op2_def);
+                       }
+                       if (op_array->opcodes[use].opcode == ZEND_OP_DATA) {
+                               if (ssa->ops[use-1].result_def >= 0) {
+                                       zend_bitset_incl(worklist, ssa->ops[use-1].result_def);
+                               }
+                               if (ssa->ops[use-1].op1_def >= 0) {
+                                       zend_bitset_incl(worklist, ssa->ops[use-1].op1_def);
+                               }
+                               if (ssa->ops[use-1].op2_def >= 0) {
+                                       zend_bitset_incl(worklist, ssa->ops[use-1].op2_def);
+                               }
+                       }
+                       use = zend_ssa_next_use(ssa->ops, var, use);
+               } while (use >= 0);
+       }
+}
+
+static void reset_dependent_vars(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+{
+       zend_ssa_op *ssa_ops = ssa->ops;
+       zend_ssa_var *ssa_vars = ssa->vars;
+       zend_ssa_var_info *ssa_var_info = ssa->var_info;
+       zend_ssa_phi *p;
+       int use;
+
+       p = ssa_vars[var].phi_use_chain;
+       while (p) {
+               if (ssa_var_info[p->ssa_var].type) {
+                       ssa_var_info[p->ssa_var].type = 0;
+                       zend_bitset_incl(worklist, p->ssa_var);
+                       reset_dependent_vars(op_array, ssa, worklist, p->ssa_var);
+               }
+               p = zend_ssa_next_use_phi(ssa, var, p);
+       }
+       use = ssa_vars[var].use_chain;
+       while (use >= 0) {
+               if (ssa_ops[use].op1_def >= 0 && ssa_var_info[ssa_ops[use].op1_def].type) {
+                       ssa_var_info[ssa_ops[use].op1_def].type = 0;
+                       zend_bitset_incl(worklist, ssa_ops[use].op1_def);
+                       reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].op1_def);
+               }
+               if (ssa_ops[use].op2_def >= 0 && ssa_var_info[ssa_ops[use].op2_def].type) {
+                       ssa_var_info[ssa_ops[use].op2_def].type = 0;
+                       zend_bitset_incl(worklist, ssa_ops[use].op2_def);
+                       reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].op2_def);
+               }
+               if (ssa_ops[use].result_def >= 0 && ssa_var_info[ssa_ops[use].result_def].type) {
+                       ssa_var_info[ssa_ops[use].result_def].type = 0;
+                       zend_bitset_incl(worklist, ssa_ops[use].result_def);
+                       reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].result_def);
+               }
+               if (op_array->opcodes[use+1].opcode == ZEND_OP_DATA) {
+                       if (ssa_ops[use+1].op1_def >= 0 && ssa_var_info[ssa_ops[use+1].op1_def].type) {
+                               ssa_var_info[ssa_ops[use+1].op1_def].type = 0;
+                               zend_bitset_incl(worklist, ssa_ops[use+1].op1_def);
+                               reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].op1_def);
+                       }
+                       if (ssa_ops[use+1].op2_def >= 0 && ssa_var_info[ssa_ops[use+1].op2_def].type) {
+                               ssa_var_info[ssa_ops[use+1].op2_def].type = 0;
+                               zend_bitset_incl(worklist, ssa_ops[use+1].op2_def);
+                               reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].op2_def);
+                       }
+                       if (ssa_ops[use+1].result_def >= 0 && ssa_var_info[ssa_ops[use+1].result_def].type) {
+                               ssa_var_info[ssa_ops[use+1].result_def].type = 0;
+                               zend_bitset_incl(worklist, ssa_ops[use+1].result_def);
+                               reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].result_def);
+                       }
+               }
+               use = zend_ssa_next_use(ssa_ops, var, use);
+       }
+#ifdef SYM_RANGE
+       /* Process symbolic control-flow constraints */
+       p = ssa->vars[var].sym_use_chain;
+       while (p) {
+               ssa_var_info[p->ssa_var].type = 0;
+               zend_bitset_incl(worklist, p->ssa_var);
+               reset_dependent_vars(op_array, ssa, worklist, p->ssa_var);
+               p = p->sym_use_chain;
+       }
+#endif
+}
+
+static void check_type_narrowing(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var, uint32_t old_type, uint32_t new_type)
+{
+       /* if new_type set resets some bits from old_type set
+        * We have completely recalculate types of some dependent SSA variables
+        * (this may occurs mainly because of incremental inter-precudure
+        * type inference)
+        */
+       if (old_type & ~new_type) {
+               reset_dependent_vars(op_array, ssa, worklist, var);
+       }
+}
+
+uint32_t zend_array_element_type(uint32_t t1, int write, int insert)
+{
+       uint32_t tmp = 0;
+
+       if (t1 & MAY_BE_OBJECT) {
+               tmp |= MAY_BE_DEF | MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+       }
+       if (t1 & MAY_BE_ARRAY) {
+               if (insert) {
+                       tmp |= MAY_BE_DEF | MAY_BE_NULL | MAY_BE_RCN;
+               } else {
+                       tmp |= MAY_BE_DEF | MAY_BE_NULL | ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+                       if (tmp & MAY_BE_ARRAY) {
+                               tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                       }
+                       if (t1 & MAY_BE_ARRAY_OF_REF) {
+                               tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+                       } else {
+                               tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+                       }
+               }
+       }
+       if (t1 & MAY_BE_STRING) {
+               tmp |= MAY_BE_DEF | MAY_BE_STRING | MAY_BE_RC1;
+               if (write) {
+                       tmp |= MAY_BE_NULL;
+               }
+       }
+       if (t1 & (MAY_BE_NULL|MAY_BE_FALSE)) {
+               tmp |= MAY_BE_DEF | MAY_BE_NULL | MAY_BE_RCN;
+               if (t1 & MAY_BE_ERROR) {
+                       if (write) {
+                               tmp |= MAY_BE_ERROR;
+                       }
+               }
+       }
+       if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) {
+               tmp |= MAY_BE_DEF | MAY_BE_NULL | MAY_BE_RCN;
+               if (write) {
+                       tmp |= MAY_BE_ERROR;
+               }
+       }
+       return tmp;
+}
+
+static void zend_update_type_info(const zend_op_array *op_array,
+                                  zend_ssa            *ssa,
+                                  const zend_script   *script,
+                                  zend_bitset          worklist,
+                                  int                  i)
+{
+       uint32_t t1, t2;
+       uint32_t tmp, orig;
+       zend_op *opline = op_array->opcodes + i;
+       zend_ssa_op *ssa_ops = ssa->ops;
+       zend_ssa_var *ssa_vars = ssa->vars;
+       zend_ssa_var_info *ssa_var_info = ssa->var_info;
+       zend_class_entry *ce;
+       int j;
+
+       if (opline->opcode == ZEND_OP_DATA &&
+           ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
+            (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
+            (opline-1)->opcode == ZEND_ASSIGN_ADD ||
+            (opline-1)->opcode == ZEND_ASSIGN_SUB ||
+            (opline-1)->opcode == ZEND_ASSIGN_MUL ||
+            (opline-1)->opcode == ZEND_ASSIGN_DIV ||
+            (opline-1)->opcode == ZEND_ASSIGN_MOD ||
+            (opline-1)->opcode == ZEND_ASSIGN_SL ||
+            (opline-1)->opcode == ZEND_ASSIGN_SR ||
+            (opline-1)->opcode == ZEND_ASSIGN_CONCAT ||
+            (opline-1)->opcode == ZEND_ASSIGN_BW_OR ||
+            (opline-1)->opcode == ZEND_ASSIGN_BW_AND ||
+            (opline-1)->opcode == ZEND_ASSIGN_BW_XOR ||
+            (opline-1)->opcode == ZEND_ASSIGN_POW ||
+            (opline-1)->opcode == ZEND_FE_FETCH_R ||
+            (opline-1)->opcode == ZEND_FE_FETCH_RW)) {
+               opline--;
+               i--;
+       }
+
+       t1 = OP1_INFO();
+       t2 = OP2_INFO();
+
+       switch (opline->opcode) {
+               case ZEND_ADD:
+                       tmp = MAY_BE_DEF | MAY_BE_RC1;
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG &&
+                               (t2 & MAY_BE_ANY) == MAY_BE_LONG) {
+
+                               if (!ssa_var_info[ssa_ops[i].result_def].has_range ||
+                                   ssa_var_info[ssa_ops[i].result_def].range.underflow ||
+                                   ssa_var_info[ssa_ops[i].result_def].range.overflow) {
+                                       /* may overflow */
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               } else {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
+                               (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                               tmp |= MAY_BE_DOUBLE;
+                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY &&
+                                          (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) {
+                               tmp |= MAY_BE_ARRAY;
+                               tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                               tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                       } else {
+                               tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               if ((t1 & MAY_BE_ARRAY) && (t2 & MAY_BE_ARRAY)) {
+                                       tmp |= MAY_BE_ARRAY;
+                                       tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                                       tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                               }
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_SUB:
+               case ZEND_MUL:
+                       tmp = MAY_BE_DEF | MAY_BE_RC1;
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG &&
+                               (t2 & MAY_BE_ANY) == MAY_BE_LONG) {
+                               if (!ssa_var_info[ssa_ops[i].result_def].has_range ||
+                                   ssa_var_info[ssa_ops[i].result_def].range.underflow ||
+                                   ssa_var_info[ssa_ops[i].result_def].range.overflow) {
+                                       /* may overflow */
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               } else {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
+                               (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                               tmp |= MAY_BE_DOUBLE;
+                       } else {
+                               tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_DIV:
+               case ZEND_POW:
+                       tmp = MAY_BE_DEF | MAY_BE_RC1;
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
+                           (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                               tmp |= MAY_BE_DOUBLE;
+                       } else {
+                               tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                       }
+                       /* Division by zero results in Inf/-Inf/Nan (double), so it doesn't need any special
+                        * handling */
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_MOD:
+                       tmp = MAY_BE_DEF|MAY_BE_RC1|MAY_BE_LONG;
+                       /* Division by zero results in an exception, so it doesn't need any special handling */
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_BW_NOT:
+                       tmp = MAY_BE_DEF | MAY_BE_RC1;
+                       if (t1 & MAY_BE_STRING) {
+                               tmp |= MAY_BE_STRING;
+                       }
+                       if (t1 & (MAY_BE_ANY-MAY_BE_STRING)) {
+                               tmp |= MAY_BE_LONG;
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_BW_OR:
+               case ZEND_BW_AND:
+               case ZEND_BW_XOR:
+                       tmp = MAY_BE_DEF | MAY_BE_RC1;
+                       if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
+                               tmp |= MAY_BE_STRING;
+                       }
+                       if ((t1 & (MAY_BE_ANY-MAY_BE_STRING)) || (t1 & (MAY_BE_ANY-MAY_BE_STRING))) {
+                               tmp |= MAY_BE_LONG;
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_SL:
+               case ZEND_SR:
+               case ZEND_BEGIN_SILENCE:
+                       UPDATE_SSA_TYPE(MAY_BE_DEF|MAY_BE_RC1|MAY_BE_LONG, ssa_ops[i].result_def);
+                       break;
+               case ZEND_BOOL_NOT:
+               case ZEND_BOOL_XOR:
+               case ZEND_IS_IDENTICAL:
+               case ZEND_IS_NOT_IDENTICAL:
+               case ZEND_IS_EQUAL:
+               case ZEND_IS_NOT_EQUAL:
+               case ZEND_IS_SMALLER:
+               case ZEND_IS_SMALLER_OR_EQUAL:
+               case ZEND_INSTANCEOF:
+               case ZEND_JMPZ_EX:
+               case ZEND_JMPNZ_EX:
+               case ZEND_CASE:
+               case ZEND_BOOL:
+               case ZEND_ISSET_ISEMPTY_VAR:
+               case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+               case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+               case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+               case ZEND_ASSERT_CHECK:
+                       UPDATE_SSA_TYPE(MAY_BE_DEF|MAY_BE_RC1|MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
+                       break;
+               case ZEND_CAST:
+                       tmp = MAY_BE_DEF|MAY_BE_RC1;
+                       if (opline->extended_value == _IS_BOOL) {
+                               tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+                       } else {
+                               tmp |= 1 << opline->extended_value;
+                       }
+                       if (opline->extended_value == IS_ARRAY) {
+                               if (t1 & MAY_BE_ARRAY) {
+                                       tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF);
+                               }
+                               if (t1 & MAY_BE_OBJECT) {
+                                       tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                               } else {
+                                       tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+                               }
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_QM_ASSIGN:
+               case ZEND_COALESCE:
+                       if (opline->op1_type == IS_CV || opline->op1_type == IS_VAR) {
+                               tmp = (MAY_BE_DEF | MAY_BE_RCN | t1) & ~(MAY_BE_UNDEF|MAY_BE_REF);
+                       } else {
+                               tmp = (MAY_BE_DEF | MAY_BE_RC1 | t1) & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RCN);
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                               UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def);
+                       } else {
+                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_ADD:
+                       orig = 0;
+                       if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = MAY_BE_ANY;
+                               t2 = OP1_DATA_INFO();
+                       } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = zend_array_element_type(t1, 1, 0);
+                               t2 = OP1_DATA_INFO();
+                       } else {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (ssa_ops[i].result_def >= 0) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t1 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                       }
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG &&
+                               (t2 & MAY_BE_ANY) == MAY_BE_LONG) {
+
+                               if (!ssa_var_info[ssa_ops[i].op1_def].has_range ||
+                                   ssa_var_info[ssa_ops[i].op1_def].range.underflow ||
+                                   ssa_var_info[ssa_ops[i].op1_def].range.overflow) {
+                                       /* may overflow */
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               } else {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
+                               (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                               tmp |= MAY_BE_DOUBLE;
+                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY &&
+                                          (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) {
+                               tmp |= MAY_BE_ARRAY;
+                               tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                               tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                       } else {
+                               tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               if ((t1 & MAY_BE_ARRAY) && (t2 & MAY_BE_ARRAY)) {
+                                       tmp |= MAY_BE_ARRAY;
+                                       tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                                       tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+                               }
+                       }
+                       if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               if (opline->op1_type == IS_CV) {
+                                       orig |= MAY_BE_ARRAY | ((tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT);
+                                       t2 = OP2_INFO();
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                                       if ((orig & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                               UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                                       } else {
+                                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                                       }
+                               }
+                       } else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               if (opline->op1_type == IS_CV) {
+                                       if (orig & (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING)) {
+                                               orig |= MAY_BE_OBJECT;
+                                       }
+                                       if (orig & MAY_BE_RCN) {
+                                               orig |= MAY_BE_RC1;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                                       if ((orig & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                               UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                                       } else {
+                                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                                       }
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_SUB:
+               case ZEND_ASSIGN_MUL:
+                       if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               goto unknown_opcode;
+                       } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = zend_array_element_type(t1, 1, 0);
+                               t2 = OP1_DATA_INFO();
+                       } else {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (ssa_ops[i].result_def >= 0) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t1 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                       }
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG &&
+                               (t2 & MAY_BE_ANY) == MAY_BE_LONG) {
+                               if (!ssa_var_info[ssa_ops[i].op1_def].has_range ||
+                                   ssa_var_info[ssa_ops[i].op1_def].range.underflow ||
+                                   ssa_var_info[ssa_ops[i].op1_def].range.overflow) {
+                                       /* may overflow */
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               } else {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
+                               (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                               tmp |= MAY_BE_DOUBLE;
+                       } else {
+                               tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                       }
+                       if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               if (opline->op1_type == IS_CV) {
+                                       orig |= MAY_BE_ARRAY | ((tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT);
+                                       t2 = OP2_INFO();
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_DIV:
+               case ZEND_ASSIGN_POW:
+                       if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               goto unknown_opcode;
+                       } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = zend_array_element_type(t1, 1, 0);
+                               t2 = OP1_DATA_INFO();
+                       } else {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (ssa_ops[i].result_def >= 0) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t1 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                       }
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
+                               (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                               tmp |= MAY_BE_DOUBLE;
+                       } else {
+                               tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                       }
+                       /* Division by zero results in Inf/-Inf/Nan (double), so it doesn't need any special
+                        * handling */
+                       if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               if (opline->op1_type == IS_CV) {
+                                       orig |= MAY_BE_ARRAY | ((tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT);
+                                       t2 = OP2_INFO();
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_MOD:
+                       if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               goto unknown_opcode;
+                       } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = zend_array_element_type(t1, 1, 0);
+                               t2 = OP1_DATA_INFO();
+                       } else {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (ssa_ops[i].result_def >= 0) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t1 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                       }
+                       tmp |= MAY_BE_LONG;
+                       if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               if (opline->op1_type == IS_CV) {
+                                       orig |= MAY_BE_ARRAY | ((tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT);
+                                       t2 = OP2_INFO();
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_SL:
+               case ZEND_ASSIGN_SR:
+                       if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               goto unknown_opcode;
+                       } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = zend_array_element_type(t1, 1, 0);
+                               t2 = OP1_DATA_INFO();
+                       } else {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (ssa_ops[i].result_def >= 0) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t1 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                       }
+                       tmp |= MAY_BE_LONG;
+                       if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               if (opline->op1_type == IS_CV) {
+                                       orig |= MAY_BE_ARRAY | ((tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT);
+                                       t2 = OP2_INFO();
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_CONCAT:
+                       if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               goto unknown_opcode;
+                       } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = zend_array_element_type(t1, 1, 0);
+                               t2 = OP1_DATA_INFO();
+                       } else {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (ssa_ops[i].result_def >= 0) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t1 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                       }
+                       tmp |= MAY_BE_STRING;
+                       if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               if (opline->op1_type == IS_CV) {
+                                       orig |= MAY_BE_ARRAY | ((tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT);
+                                       t2 = OP2_INFO();
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_BW_OR:
+               case ZEND_ASSIGN_BW_AND:
+               case ZEND_ASSIGN_BW_XOR:
+                       if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+                               goto unknown_opcode;
+                       } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1;
+                               orig = t1;
+                               t1 = zend_array_element_type(t1, 1, 0);
+                               t2 = OP1_DATA_INFO();
+                       } else {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (ssa_ops[i].result_def >= 0) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t1 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                       }
+                       if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
+                               tmp |= MAY_BE_STRING;
+                       }
+                       if ((t1 & (MAY_BE_ANY-MAY_BE_STRING)) || (t1 & (MAY_BE_ANY-MAY_BE_STRING))) {
+                               tmp |= MAY_BE_LONG;
+                       }
+                       if (opline->extended_value == ZEND_ASSIGN_DIM) {
+                               if (opline->op1_type == IS_CV) {
+                                       orig |= MAY_BE_ARRAY | ((tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT);
+                                       t2 = OP2_INFO();
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                                       UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+                               }
+                       } else {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+// TODO: ???
+//                     UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_ops[i].op1_def);
+//                     if (ssa_ops[i].result_def >= 0) {
+//                             UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_ops[i].result_def);
+//                     }
+//                     break;
+               case ZEND_PRE_INC:
+               case ZEND_PRE_DEC:
+                       tmp = MAY_BE_DEF;
+                       if (t1 & MAY_BE_REF) {
+                               tmp |= MAY_BE_REF;
+                       }
+                       if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                               tmp |= MAY_BE_RC1;
+                               if (ssa_ops[i].result_def >= 0) {
+                                       tmp |= MAY_BE_RCN;
+                               }
+                       }
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG) {
+                               if (!ssa_var_info[ssa_ops[i].op1_use].has_range ||
+                                   (opline->opcode == ZEND_PRE_DEC &&
+                                    (ssa_var_info[ssa_ops[i].op1_use].range.underflow ||
+                                     ssa_var_info[ssa_ops[i].op1_use].range.min == LONG_MIN)) ||
+                                    (opline->opcode == ZEND_PRE_INC &&
+                                     (ssa_var_info[ssa_ops[i].op1_use].range.overflow ||
+                                      ssa_var_info[ssa_ops[i].op1_use].range.min == LONG_MAX))) {
+                                       /* may overflow */
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               } else {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                       } else {
+                               if (t1 & MAY_BE_NULL) {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                               if (t1 & MAY_BE_LONG) {
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               }
+                               if (t1 & MAY_BE_DOUBLE) {
+                                       tmp |= MAY_BE_DOUBLE;
+                               }
+                               if (t1 & MAY_BE_STRING) {
+                                       tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+                               }
+                               tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+                       }
+                       if (ssa_ops[i].op1_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_POST_INC:
+               case ZEND_POST_DEC:
+                       if (ssa_ops[i].result_def >= 0) {
+                               tmp = (MAY_BE_DEF | MAY_BE_RC1 | t1) & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RCN);
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       tmp = MAY_BE_DEF;
+                       if (t1 & MAY_BE_REF) {
+                               tmp |= MAY_BE_REF;
+                       }
+                       if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                               tmp |= MAY_BE_RC1;
+                       }
+                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG) {
+                               if (!ssa_var_info[ssa_ops[i].op1_use].has_range ||
+                                    (opline->opcode == ZEND_PRE_DEC &&
+                                     (ssa_var_info[ssa_ops[i].op1_use].range.underflow ||
+                                      ssa_var_info[ssa_ops[i].op1_use].range.min == LONG_MIN)) ||
+                                     (opline->opcode == ZEND_PRE_INC &&
+                                      (ssa_var_info[ssa_ops[i].op1_use].range.overflow ||
+                                       ssa_var_info[ssa_ops[i].op1_use].range.min == LONG_MAX))) {
+                                       /* may overflow */
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               } else {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                       } else {
+                               if (t1 & MAY_BE_NULL) {
+                                       tmp |= MAY_BE_LONG;
+                               }
+                               if (t1 & MAY_BE_LONG) {
+                                       tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+                               }
+                               if (t1 & MAY_BE_DOUBLE) {
+                                       tmp |= MAY_BE_DOUBLE;
+                               }
+                               if (t1 & MAY_BE_STRING) {
+                                       tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+                               }
+                               tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+                       }
+                       if (ssa_ops[i].op1_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_DIM:
+                       if (opline->op1_type == IS_CV) {
+                               tmp = MAY_BE_DEF | (t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF));
+                               tmp &= ~MAY_BE_NULL;
+                               if (t1 & (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING)) {
+                                       tmp |= MAY_BE_ARRAY;
+                               }
+                               if (tmp & MAY_BE_RCN) {
+                                       tmp |= MAY_BE_RC1;
+                               }
+                               if (tmp & MAY_BE_ARRAY) {
+                                       tmp |= (OP1_DATA_INFO() & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                               if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               tmp = MAY_BE_DEF;
+                               if (t1 & MAY_BE_STRING) {
+                                       tmp |= MAY_BE_STRING;
+                               }
+                               if (t1 & (MAY_BE_ANY - MAY_BE_STRING)) {
+                                       tmp |= (OP1_DATA_INFO() & (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+                               }
+                               tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+                               if (t1 & MAY_BE_OBJECT) {
+                                       tmp |= MAY_BE_REF;
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       if ((opline+1)->op1_type == IS_CV) {
+                               opline++;
+                               i++;
+                               tmp = OP1_INFO();
+                               if (tmp & MAY_BE_DEF) {
+                                       if (tmp & MAY_BE_RC1) {
+                                               if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                                       tmp |= MAY_BE_RCN;
+                                               }
+                                       }
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       break;
+               case ZEND_ASSIGN_OBJ:
+                       if (opline->op1_type == IS_CV) {
+                               tmp = MAY_BE_DEF | (t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF));
+                               tmp &= ~MAY_BE_NULL;
+                               if (t1 & (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING)) {
+                                       tmp |= MAY_BE_OBJECT;
+                               }
+                               if (tmp & MAY_BE_RCN) {
+                                       tmp |= MAY_BE_RC1;
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                               if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               // TODO: ???
+                               tmp = MAY_BE_DEF | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       if ((opline+1)->op1_type == IS_CV) {
+                               opline++;
+                               i++;
+                               tmp = OP1_INFO();
+                               if (tmp & MAY_BE_DEF) {
+                                       if (tmp & MAY_BE_RC1) {
+                                               if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                                       tmp |= MAY_BE_RCN;
+                                               }
+                                       }
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       break;
+               case ZEND_ASSIGN:
+                       if (opline->op2_type == IS_CV) {
+                               tmp = t2;
+                               if (tmp & MAY_BE_DEF) {
+                                       if (tmp & MAY_BE_RC1) {
+                                               if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
+                                                       tmp |= MAY_BE_RCN;
+                                               }
+                                       }
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+                       }
+                       tmp = (MAY_BE_DEF | t2) & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
+                       if (t1 & MAY_BE_REF) {
+                               tmp |= MAY_BE_REF;
+                       }
+                       if ((t1 & MAY_BE_RCN) && !(opline->op2_type & (IS_CV|IS_VAR))) {
+                               tmp |= MAY_BE_RC1;
+                       }
+                       if ((t1 & MAY_BE_RCN) && (opline->op2_type & (IS_CV|IS_VAR))) {
+                               if (t2 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_RC1;
+                               }
+                               if (t2 & MAY_BE_RC1) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (opline->op2_type == IS_CV) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t2 & MAY_BE_RCN) {
+                                       tmp |= MAY_BE_RCN;
+                               }
+                       }
+                       if ((t1 & MAY_BE_RC1) && !(opline->op2_type & (IS_CV|IS_VAR))) {
+                               tmp |= MAY_BE_RC1;
+                       }
+                       if ((t1 & MAY_BE_RC1) && (opline->op2_type & (IS_CV|IS_VAR))) {
+                               if (t2 & MAY_BE_REF) {
+                                       tmp |= MAY_BE_RC1;
+                               }
+                               if (t2 & MAY_BE_RC1) {
+                                       tmp |= MAY_BE_RC1;
+                                       if (opline->op2_type == IS_CV) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               if (t2 & MAY_BE_RCN) {
+                                       tmp |= MAY_BE_RCN;
+                               }
+                       }
+                       if (RETURN_VALUE_USED(opline) && (tmp & MAY_BE_RC1)) {
+                               tmp |= MAY_BE_RCN;
+                       }
+                       if (ssa_ops[i].op1_def >= 0) {
+                               if (ssa_var_info[ssa_ops[i].op1_def].use_as_double) {
+                                       tmp &= ~MAY_BE_LONG;
+                                       tmp |= MAY_BE_DOUBLE;
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                               if ((t2 & MAY_BE_OBJECT) && ssa_ops[i].op2_use >= 0 && ssa_var_info[ssa_ops[i].op2_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op2_use].ce, ssa_var_info[ssa_ops[i].op2_use].is_instanceof, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                               if ((t2 & MAY_BE_OBJECT) && ssa_ops[i].op2_use >= 0 && ssa_var_info[ssa_ops[i].op2_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op2_use].ce, ssa_var_info[ssa_ops[i].op2_use].is_instanceof, ssa_ops[i].result_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                               }
+                       }
+                       break;
+               case ZEND_ASSIGN_REF:
+// TODO: ???
+                       if (opline->op2_type == IS_CV) {
+                               tmp = (MAY_BE_DEF | MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+                       }
+                       if (opline->op2_type == IS_VAR && opline->extended_value == ZEND_RETURNS_FUNCTION) {
+                               tmp = (MAY_BE_DEF | MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF;
+                       } else {
+                               tmp = (MAY_BE_DEF | MAY_BE_REF | t2) & ~(MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN);
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       if (ssa_ops[i].result_def >= 0) {
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_BIND_GLOBAL:
+                       tmp = (MAY_BE_DEF | MAY_BE_REF | MAY_BE_ANY );
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       break;
+               case ZEND_SEND_VAR:
+                       UPDATE_SSA_TYPE(t1 | MAY_BE_RC1 | MAY_BE_RCN, ssa_ops[i].op1_def);
+                       if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                               UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                       } else {
+                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                       }
+                       break;
+               case ZEND_SEND_VAR_EX:
+               case ZEND_SEND_VAR_NO_REF:
+               case ZEND_SEND_REF:
+// TODO: ???
+                       if (ssa_ops[i].op1_def >= 0) {
+                               tmp = (t1 & MAY_BE_UNDEF)|MAY_BE_DEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       break;
+               case ZEND_FAST_CONCAT:
+               case ZEND_ROPE_INIT:
+               case ZEND_ROPE_ADD:
+               case ZEND_ROPE_END:
+                       UPDATE_SSA_TYPE(MAY_BE_DEF|MAY_BE_RC1|MAY_BE_STRING, ssa_ops[i].result_def);
+                       break;
+               case ZEND_CONCAT:
+                       /* TODO: +MAY_BE_OBJECT ??? */
+                       UPDATE_SSA_TYPE(MAY_BE_DEF|MAY_BE_RC1|MAY_BE_STRING, ssa_ops[i].result_def);
+                       break;
+               case ZEND_RECV:
+               case ZEND_RECV_INIT:
+               {
+                       /* Typehinting */
+                       zend_func_info *func_info;
+                       zend_arg_info *arg_info = NULL;
+                       if (op_array->arg_info && opline->op1.num <= op_array->num_args) {
+                               arg_info = &op_array->arg_info[opline->op1.num-1];
+                       }
+
+                       ce = NULL;
+                       if (arg_info) {
+                               tmp = MAY_BE_DEF;
+                               if (arg_info->class_name) {
+                                       // class type hinting...
+                                       zend_string *lcname = zend_string_tolower(arg_info->class_name);
+                                       tmp |= MAY_BE_OBJECT;
+                                       ce = zend_hash_find_ptr(&script->class_table, lcname);
+                                       if (!ce) {
+                                               ce = zend_hash_find_ptr(CG(class_table), lcname);
+                                               if (ce && ce->type != ZEND_INTERNAL_CLASS) {
+                                                       ce = NULL;
+                                               }
+                                       }
+                                       zend_string_release(lcname);
+                               } else if (arg_info->type_hint != IS_UNDEF) {
+                                       if (arg_info->type_hint == IS_CALLABLE) {
+                                               tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+                                       } else if (arg_info->type_hint == IS_ARRAY) {
+                                               tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+                                       } else if (arg_info->type_hint == _IS_BOOL) {
+                                               tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+                                       } else {
+                                               ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
+                                               tmp |= 1 << arg_info->type_hint;
+                                       }
+                               } else {
+                                       tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+                               }
+                               if (arg_info->allow_null) {
+                                       tmp |= MAY_BE_NULL;
+                               } else if (opline->opcode == ZEND_RECV_INIT &&
+                                          Z_CONSTANT_P(CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants))) {
+                                       /* The constant may resolve to NULL */
+                                       tmp |= MAY_BE_NULL;
+                               }
+                               if (arg_info->pass_by_reference) {
+                                       tmp |= MAY_BE_REF;
+                               } else {
+                                       tmp |= MAY_BE_RC1|MAY_BE_RCN;
+                               }
+                       } else {
+                               tmp = MAY_BE_DEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+                       }
+                       func_info = ZEND_FUNC_INFO(op_array);
+                       if (func_info && (int)opline->op1.num-1 < func_info->num_args) {
+                               tmp = (tmp & (MAY_BE_DEF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF)) |
+                                       (tmp & func_info->arg_info[opline->op1.num-1].info.type);
+                       } else {
+                               if (opline->opcode == ZEND_RECV && (!arg_info || arg_info->type_hint == IS_UNDEF)) {
+                                       /* If the argument has no default value and no typehint, it is possible
+                                        * to pass less arguments than the function expects */
+                                       tmp |= MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_RC1;
+                               }
+                       }
+#if 0
+                       /* We won't recieve unused arguments */
+                       if (ssa_vars[ssa_ops[i].result_def].use_chain < 0 &&
+                           ssa_vars[ssa_ops[i].result_def].phi_use_chain == NULL &&
+                           op_array->arg_info &&
+                           opline->op1.num <= op_array->num_args &&
+                           op_array->arg_info[opline->op1.num-1].class_name == NULL &&
+                           !op_array->arg_info[opline->op1.num-1].type_hint) {
+                               tmp = MAY_BE_UNDEF|MAY_BE_RCN|MAY_BE_NULL;
+                       }
+#endif
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       if (func_info &&
+                           (int)opline->op1.num-1 < func_info->num_args &&
+                           func_info->arg_info[opline->op1.num-1].info.ce) {
+                               UPDATE_SSA_OBJ_TYPE(
+                                       func_info->arg_info[opline->op1.num-1].info.ce,
+                                       func_info->arg_info[opline->op1.num-1].info.is_instanceof,
+                                       ssa_ops[i].result_def);
+                       } else if (ce) {
+                               UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def);
+                       } else {
+                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                       }
+                       break;
+               }
+               case ZEND_DECLARE_CLASS:
+               case ZEND_DECLARE_INHERITED_CLASS:
+                       UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def);
+                       if ((ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants)))) != NULL) {
+                               UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_FETCH_CLASS:
+                       UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def);
+                       if (opline->op2_type == IS_UNUSED) {
+                               switch (opline->extended_value & ZEND_FETCH_CLASS_MASK) {
+                                       case ZEND_FETCH_CLASS_SELF:
+                                               if (op_array->scope) {
+                                                       UPDATE_SSA_OBJ_TYPE(op_array->scope, 0, ssa_ops[i].result_def);
+                                               } else {
+                                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                                               }
+                                               break;
+                                       case ZEND_FETCH_CLASS_PARENT:
+                                               if (op_array->scope && op_array->scope->parent) {
+                                                       UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_ops[i].result_def);
+                                               } else {
+                                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                                               }
+                                               break;
+                                       case ZEND_FETCH_CLASS_STATIC:
+                                       default:
+                                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                                               break;
+                               }
+                       } else if (opline->op2_type == IS_CONST) {
+                               zval *zv = CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants);
+                               if (Z_TYPE_P(zv) == IS_STRING) {
+                                       if ((ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(zv+1))) != NULL) {
+                                               UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+                                       } else if ((ce = zend_hash_find_ptr(CG(class_table), Z_STR_P(zv+1))) != NULL &&
+                                                  ce->type == ZEND_INTERNAL_CLASS) {
+                                               UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+                                       } else {
+                                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                                       }
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                               }
+                       } else if (t2 & MAY_BE_OBJECT) {
+                               if (ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def);
+                               }
+                       } else {
+                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_NEW:
+                       tmp = MAY_BE_DEF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT;
+                       if (opline->op1_type == IS_CONST &&
+                           (ce = zend_hash_find_ptr(CG(class_table), Z_STR_P(CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants)+1))) != NULL) {
+                               UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+                       } else if ((t1 & MAY_BE_CLASS) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                               UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def);
+                       } else {
+                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_CLONE:
+                       /* FIXME: For some reason "clone" return reference */
+                       UPDATE_SSA_TYPE(MAY_BE_DEF|MAY_BE_REF|MAY_BE_OBJECT, ssa_ops[i].result_def);
+                       if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                               UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def);
+                       } else {
+                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_INIT_ARRAY:
+               case ZEND_ADD_ARRAY_ELEMENT:
+                       if (opline->op1_type == IS_CV) {
+                               if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+                                       tmp = (MAY_BE_DEF | MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+                               } else if ((t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN)) == MAY_BE_REF) {
+                                       tmp = (MAY_BE_DEF | MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+                               } else if (t1 & MAY_BE_REF) {
+                                       tmp = (MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | t1);
+                               } else {
+                                       tmp = t1;
+                                       if (t1 & MAY_BE_RC1) {
+                                               tmp |= MAY_BE_RCN;
+                                       }
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               tmp = MAY_BE_DEF|MAY_BE_RC1|MAY_BE_ARRAY;
+                               if (opline->op1_type != IS_UNUSED) {
+                                       tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+                                       if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+                                               tmp |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+                                       }
+                               }
+                               if (ssa_ops[i].result_use >= 0) {
+                                       tmp |= ssa_var_info[ssa_ops[i].result_use].type;
+                               }
+                               if (opline->op2_type == IS_UNUSED) {
+                                       tmp |= MAY_BE_ARRAY_KEY_LONG;
+                               } else {
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                               if (opline->op2_type != IS_CONST) {
+                                                       // FIXME: numeric string
+                                                       tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                               }
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_UNSET_VAR:
+                       if (opline->extended_value & ZEND_QUICK_SET) {
+                               UPDATE_SSA_TYPE((MAY_BE_NULL|MAY_BE_UNDEF|MAY_BE_RCN), ssa_ops[i].op1_def);
+                       }
+                       break;
+               case ZEND_UNSET_DIM:
+               case ZEND_UNSET_OBJ:
+                       if (ssa_ops[i].op1_def >= 0) {
+                               UPDATE_SSA_TYPE(t1, ssa_ops[i].op1_def);
+                               if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       break;
+//             case ZEND_INCLUDE_OR_EVAL:
+//             case ZEND_ISSET_ISEMPTY_VAR:
+// TODO: ???
+//                     break;
+               case ZEND_FE_RESET_R:
+               case ZEND_FE_RESET_RW:
+                       if (ssa_ops[i].op1_def) {
+                               tmp = t1;
+                               if (t1 & MAY_BE_RCN) {
+                                       tmp |= MAY_BE_RC1;
+                               }
+                               if (opline->opcode == ZEND_FE_RESET_RW) {
+//???
+                                       tmp |= MAY_BE_REF;
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                               if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       if (opline->opcode == ZEND_FE_RESET_RW) {
+//???
+                               tmp = MAY_BE_DEF | MAY_BE_REF | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT));
+                       } else if (opline->op1_type == IS_TMP_VAR || opline->op1_type == IS_CONST) {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1 | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+                       } else {
+                               tmp = MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_RCN | (t1 & (MAY_BE_REF | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                               UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def);
+                       } else {
+                               UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_FE_FETCH_R:
+               case ZEND_FE_FETCH_RW:
+                       if (t1 & MAY_BE_OBJECT) {
+                               if (opline->opcode == ZEND_FE_FETCH_RW) {
+                                       tmp = MAY_BE_DEF | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                               } else {
+                                       tmp = MAY_BE_DEF | MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                               }
+                       } else if (t1 & MAY_BE_ARRAY) {
+                               if (opline->opcode == ZEND_FE_FETCH_RW) {
+                                       tmp = MAY_BE_DEF | MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                               } else {
+                                       tmp = MAY_BE_DEF | ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+                                       if (tmp & MAY_BE_ARRAY) {
+                                               tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                                       }
+                                       if (t1 & MAY_BE_ARRAY_OF_REF) {
+                                               tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+                                       } else {
+                                               tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+                                       }
+                               }
+                       } else {
+                               if (opline->opcode == ZEND_FE_FETCH_RW) {
+                                       tmp = MAY_BE_DEF | MAY_BE_REF;
+                               } else {
+                                       tmp = MAY_BE_DEF | MAY_BE_RCN;
+                               }
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+                       if (opline->result_type == IS_TMP_VAR) {
+                               if (ssa_ops[i].result_def >= 0) {
+                                       tmp = MAY_BE_DEF | MAY_BE_RC1;
+                                       if (t1 & MAY_BE_OBJECT) {
+                                               tmp |= MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                                       } else if (t1 & MAY_BE_ARRAY) {
+                                               if (t1 & MAY_BE_ARRAY_KEY_LONG) {
+                                                       tmp |= MAY_BE_LONG;
+                                               }
+                                               if (t1 & MAY_BE_ARRAY_KEY_STRING) {
+                                                       tmp |= MAY_BE_STRING;
+                                               }
+                                               if (!(tmp & (MAY_BE_LONG|MAY_BE_STRING))) {
+                                                       tmp |= MAY_BE_NULL;
+                                               }
+                                       }
+                                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                               }
+                       }
+                       break;
+//             case ZEND_CATCH:
+// TODO: ???
+//                     break;
+//             case ZEND_JMP_SET:
+//             case ZEND_COALESCE:
+// TODO: ???
+//                     break;
+               case ZEND_FETCH_DIM_R:
+               case ZEND_FETCH_DIM_IS:
+               case ZEND_FETCH_DIM_RW:
+               case ZEND_FETCH_DIM_W:
+               case ZEND_FETCH_DIM_UNSET:
+               case ZEND_FETCH_DIM_FUNC_ARG:
+                       if (ssa_ops[i].op1_def >= 0) {
+                               tmp = t1;
+                               if (opline->opcode == ZEND_FETCH_DIM_W ||
+                                   opline->opcode == ZEND_FETCH_DIM_RW ||
+                                   opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
+                                       if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
+                                               tmp &= ~MAY_BE_UNDEF;
+                                               if (t1 & MAY_BE_NULL) {
+                                                       tmp &= ~MAY_BE_NULL;
+                                                       tmp |= MAY_BE_ARRAY;
+                                               } else if (t1 & (MAY_BE_FALSE|MAY_BE_STRING)) {
+                                                       tmp |= MAY_BE_ARRAY;
+                                               }
+                                       }
+                                       tmp |= MAY_BE_DEF;
+                                       if (tmp & MAY_BE_RCN) {
+                                               tmp |= MAY_BE_RC1;
+                                       }
+                                       if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_STRING)) {
+                                               // FIXME: numeric string
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+                                       }
+                                       if (t2 & (MAY_BE_NULL)) {
+                                               tmp |= MAY_BE_ARRAY_KEY_STRING;
+                                       }
+
+                               }
+                               ZEND_ASSERT(!ssa_vars[ssa_ops[i].result_def].phi_use_chain);
+                               j = ssa_vars[ssa_ops[i].result_def].use_chain;
+                               while (j >= 0) {
+                                       switch (op_array->opcodes[j].opcode) {
+                                               case ZEND_FETCH_DIM_W:
+                                               case ZEND_FETCH_DIM_RW:
+                                               case ZEND_FETCH_DIM_FUNC_ARG:
+                                               case ZEND_ASSIGN_ADD:
+                                               case ZEND_ASSIGN_SUB:
+                                               case ZEND_ASSIGN_MUL:
+                                               case ZEND_ASSIGN_DIV:
+                                               case ZEND_ASSIGN_MOD:
+                                               case ZEND_ASSIGN_SL:
+                                               case ZEND_ASSIGN_SR:
+                                               case ZEND_ASSIGN_CONCAT:
+                                               case ZEND_ASSIGN_BW_OR:
+                                               case ZEND_ASSIGN_BW_AND:
+                                               case ZEND_ASSIGN_BW_XOR:
+                                               case ZEND_ASSIGN_POW:
+                                               case ZEND_ASSIGN_DIM:
+                                                       tmp |= MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY;
+                                                       break;
+                                               case ZEND_SEND_VAR:
+                                                       break;
+                                               case ZEND_SEND_VAR_EX:
+                                               case ZEND_SEND_VAR_NO_REF:
+                                               case ZEND_SEND_REF:
+                                               case ZEND_ASSIGN_REF:
+                                                       tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                                                       break;
+                                               case ZEND_PRE_INC:
+                                               case ZEND_PRE_DEC:
+                                               case ZEND_POST_INC:
+                                               case ZEND_POST_DEC:
+                                                       if (tmp & MAY_BE_ARRAY_OF_LONG) {
+                                                               /* may overflow */
+                                                               tmp |= MAY_BE_ARRAY_OF_DOUBLE;
+                                                       } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) {
+                                                               tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE;
+                                                       }
+                                                       break;
+                                               default :
+                                                       break;
+                                       }
+                                       j = zend_ssa_next_use(ssa_ops, ssa_ops[i].result_def, j);
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                               if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       tmp = zend_array_element_type(
+                               t1,
+                               (opline->opcode != ZEND_FETCH_DIM_R && opline->opcode != ZEND_FETCH_DIM_IS),
+                               opline->op2_type == IS_UNUSED);
+                       if (opline->opcode == ZEND_FETCH_DIM_W ||
+                           opline->opcode == ZEND_FETCH_DIM_RW ||
+                           opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
+                               if (t1 & (MAY_BE_ERROR|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE|MAY_BE_OBJECT)) {
+                                       tmp |= MAY_BE_ERROR;
+                               } else if (opline->op2_type == IS_UNUSED) {
+                                       tmp |= MAY_BE_ERROR;
+                               } else if (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
+                                       tmp |= MAY_BE_ERROR;
+                               }
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_FETCH_OBJ_R:
+               case ZEND_FETCH_OBJ_IS:
+               case ZEND_FETCH_OBJ_RW:
+               case ZEND_FETCH_OBJ_W:
+               case ZEND_FETCH_OBJ_UNSET:
+               case ZEND_FETCH_OBJ_FUNC_ARG:
+                       if (ssa_ops[i].op1_def >= 0) {
+                               tmp = t1;
+                               if (opline->opcode == ZEND_FETCH_OBJ_W ||
+                                   opline->opcode == ZEND_FETCH_OBJ_RW ||
+                                   opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) {
+                                       if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
+                                               tmp &= ~MAY_BE_UNDEF;
+                                               if (t1 & MAY_BE_NULL) {
+                                                       tmp &= ~MAY_BE_NULL;
+                                                       tmp |= MAY_BE_OBJECT;
+                                               } else if (t1 & (MAY_BE_FALSE|MAY_BE_STRING)) {
+                                                       tmp |= MAY_BE_OBJECT;
+                                               }
+                                       }
+                                       tmp |= MAY_BE_DEF;
+                                       if (tmp & MAY_BE_RCN) {
+                                               tmp |= MAY_BE_RC1;
+                                       }
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                               if ((t1 & MAY_BE_OBJECT) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].op1_def);
+                               } else {
+                                       UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+                               }
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               tmp = MAY_BE_DEF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ERROR;
+                               if (opline->result_type == IS_TMP_VAR) {
+                                       tmp |= MAY_BE_RC1;
+                               } else {
+                                       tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+               case ZEND_DO_FCALL:
+               case ZEND_DO_ICALL:
+               case ZEND_DO_UCALL:
+               case ZEND_DO_FCALL_BY_NAME:
+                       if (ssa_ops[i].result_def >= 0) {
+                               zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+                               zend_call_info *call_info;
+
+                               if (!func_info) {
+                                       goto unknown_opcode;
+                               }
+                               call_info = func_info->callee_info;
+                               while (call_info && call_info->caller_call_opline != opline) {
+                                       call_info = call_info->next_callee;
+                               }
+                               if (!call_info) {
+                                       goto unknown_opcode;
+                               }
+                               tmp = zend_get_func_info(call_info, ssa) & ~(FUNC_MAY_WARN|FUNC_MAY_INLINE);
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                               if (call_info->callee_func->type == ZEND_USER_FUNCTION) {
+                                       func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array);
+                                       if (func_info) {
+                                               UPDATE_SSA_OBJ_TYPE(
+                                                       func_info->return_info.ce,
+                                                       func_info->return_info.is_instanceof,
+                                                       ssa_ops[i].result_def);
+                                       }
+                               }
+                       }
+                       break;
+               case ZEND_FETCH_CONSTANT:
+               case ZEND_FETCH_CLASS_CONSTANT:
+                       UPDATE_SSA_TYPE(MAY_BE_DEF|MAY_BE_RC1|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_ops[i].result_def);
+                       break;
+               case ZEND_STRLEN:
+                       tmp = MAY_BE_DEF|MAY_BE_RC1|MAY_BE_LONG;
+                       if (t1 & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) {
+                               tmp |= MAY_BE_NULL;
+                       }
+                       UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       break;
+               case ZEND_TYPE_CHECK:
+               case ZEND_DEFINED:
+                       UPDATE_SSA_TYPE(MAY_BE_DEF|MAY_BE_RC1|MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
+                       break;
+               default:
+unknown_opcode:
+                       if (ssa_ops[i].op1_def >= 0) {
+                               tmp = MAY_BE_DEF | MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+                       }
+                       if (ssa_ops[i].result_def >= 0) {
+                               tmp = MAY_BE_DEF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+                               if (opline->result_type == IS_TMP_VAR) {
+                                       tmp |= MAY_BE_RC1;
+                               } else {
+                                       tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+                               }
+                               UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+                       }
+                       break;
+       }
+}
+
+int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist)
+{
+       zend_basic_block *blocks = ssa->cfg.blocks;
+       zend_ssa_var *ssa_vars = ssa->vars;
+       zend_ssa_var_info *ssa_var_info = ssa->var_info;
+       int ssa_vars_count = ssa->vars_count;
+       uint i;
+       int j;
+       uint32_t tmp;
+
+       while (!zend_bitset_empty(worklist, zend_bitset_len(ssa_vars_count))) {
+               j = zend_bitset_first(worklist, zend_bitset_len(ssa_vars_count));
+               zend_bitset_excl(worklist, j);
+               if (ssa_vars[j].definition_phi) {
+                       zend_ssa_phi *p = ssa_vars[j].definition_phi;
+                       if (p->pi >= 0) {
+                               tmp = get_ssa_var_info(ssa, p->sources[0]);
+                               UPDATE_SSA_TYPE(tmp, j);
+                               if (ssa_var_info[p->sources[0]].ce) {
+                                       UPDATE_SSA_OBJ_TYPE(ssa_var_info[p->sources[0]].ce, ssa_var_info[p->sources[0]].is_instanceof, j);
+                               }
+                       } else {
+                               int first = 1;
+                               int is_instanceof = 0;
+                               zend_class_entry *ce = NULL;
+
+                               tmp = 0;
+                               for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+                                       tmp |= get_ssa_var_info(ssa, p->sources[i]);
+                               }
+                               UPDATE_SSA_TYPE(tmp, j);
+                               for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+                                       if (get_ssa_var_info(ssa, p->sources[i])) {
+                                               if (first) {
+                                                       ce = ssa_var_info[p->sources[i]].ce;
+                                                       is_instanceof = ssa_var_info[p->sources[i]].is_instanceof;
+                                                       first = 0;
+                                               } else if (ce != ssa_var_info[p->sources[i]].ce) {
+                                                       ce = NULL;
+                                                       is_instanceof = 0;
+                                               } else if (is_instanceof != ssa_var_info[p->sources[i]].is_instanceof) {
+                                                       is_instanceof = 1;
+                                               }
+                                       }
+                               }
+                               UPDATE_SSA_OBJ_TYPE(ce, is_instanceof, j);
+                       }
+               } else if (ssa_vars[j].definition >= 0) {
+                       i = ssa_vars[j].definition;
+                       zend_update_type_info(op_array, ssa, script, worklist, i);
+               }
+       }
+       return SUCCESS;
+}
+
+#define CHECK_MAY_BE_USED_AS_DOUBLE(var2) do { \
+               if ((ssa->var_info[var2].type & MAY_BE_ANY) == (MAY_BE_LONG | MAY_BE_DOUBLE)) { \
+                       if (ssa->vars[var2].var < op_array->last_var) { \
+                               return 0; \
+                       } else if (ssa->vars[var2].definition >= 0 && \
+                                  op_array->opcodes[ssa->vars[var2].definition].opcode != ZEND_ADD && \
+                                  op_array->opcodes[ssa->vars[var2].definition].opcode != ZEND_SUB && \
+                                  op_array->opcodes[ssa->vars[var2].definition].opcode != ZEND_MUL && \
+                                  op_array->opcodes[ssa->vars[var2].definition].opcode != ZEND_DIV) { \
+                               return 0; \
+                       } else if (!zend_may_be_used_as_double(op_array, ssa, var2)) { \
+                               return 0; \
+                       } \
+               } \
+       } while (0)
+
+static int zend_may_be_used_as_double(const zend_op_array *op_array, zend_ssa *ssa, int var)
+{
+       FOR_EACH_VAR_USAGE(var, CHECK_MAY_BE_USED_AS_DOUBLE);
+       return 1;
+}
+
+static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa)
+{
+       zend_ssa_op *ssa_ops = ssa->ops;
+       zend_ssa_var *ssa_vars = ssa->vars;
+       zend_ssa_var_info *ssa_var_info = ssa->var_info;
+       int ssa_vars_count = ssa->vars_count;
+       int j;
+       zend_bitset worklist, types;
+
+       types = alloca(sizeof(zend_ulong) * op_array->last_var);
+       memset(types, 0, sizeof(zend_ulong) * op_array->last_var);
+       worklist = alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+       memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+       /* Find variables that may be only LONG or DOUBLE */
+       for (j = op_array->last_var; j < ssa_vars_count; j++) {
+               if (ssa_vars[j].var < op_array->last_var) {
+                       types[ssa_vars[j].var] |= ssa_var_info[j].type & (MAY_BE_ANY - MAY_BE_NULL);
+               }
+       }
+       for (j = 0; j < op_array->last_var; j++) {
+               if (types[j] == (MAY_BE_LONG | MAY_BE_DOUBLE)) {
+                       zend_bitset_incl(worklist, j);
+               }
+       }
+       if (zend_bitset_empty(worklist, zend_bitset_len(ssa_vars_count))) {
+               return SUCCESS;
+       }
+
+       /* Exclude variables that can't be narrowed */
+       for (j = op_array->last_var; j < ssa_vars_count; j++) {
+               if (ssa_vars[j].var < op_array->last_var &&
+                   zend_bitset_in(worklist, ssa_vars[j].var)) {
+                       if (ssa_vars[j].definition >= 0) {
+                               if ((ssa_var_info[j].type & MAY_BE_ANY) == MAY_BE_LONG) {
+                                       if (ssa_vars[j].use_chain >= 0 ||
+                                           op_array->opcodes[ssa_vars[j].definition].opcode != ZEND_ASSIGN ||
+                                           op_array->opcodes[ssa_vars[j].definition].op2_type != IS_CONST) {
+                                               zend_bitset_excl(worklist, ssa_vars[j].var);
+                                       }
+                               } else if ((ssa_var_info[j].type & MAY_BE_ANY) != MAY_BE_DOUBLE) {
+                                       zend_bitset_excl(worklist, ssa_vars[j].var);
+                               }
+                       }
+               }
+       }
+       if (zend_bitset_empty(worklist, zend_bitset_len(ssa_vars_count))) {
+               return SUCCESS;
+       }
+
+       for (j = op_array->last_var; j < ssa_vars_count; j++) {
+               if (ssa_vars[j].var < op_array->last_var &&
+                   zend_bitset_in(worklist, ssa_vars[j].var) &&
+                   (ssa_var_info[j].type & (MAY_BE_ANY-MAY_BE_NULL)) == (MAY_BE_LONG|MAY_BE_DOUBLE) &&
+                   ssa_vars[j].use_chain >= 0) {
+                       if (!zend_may_be_used_as_double(op_array, ssa, j)) {
+                               zend_bitset_excl(worklist, ssa_vars[j].var);
+                       }
+               }
+       }
+       if (zend_bitset_empty(worklist, zend_bitset_len(ssa_vars_count))) {
+               return SUCCESS;
+       }
+
+       for (j = op_array->last_var; j < ssa_vars_count; j++) {
+               if (ssa_vars[j].var < op_array->last_var &&
+                   zend_bitset_in(worklist, ssa_vars[j].var)) {
+                       if ((ssa_var_info[j].type & MAY_BE_ANY) == MAY_BE_LONG &&
+                           ssa_vars[j].definition >= 0 &&
+                           ssa_ops[ssa_vars[j].definition].result_def < 0 &&
+                           op_array->opcodes[ssa_vars[j].definition].opcode == ZEND_ASSIGN &&
+                           op_array->opcodes[ssa_vars[j].definition].op2_type == IS_CONST &&
+                           Z_TYPE_P(CRT_CONSTANT_EX(op_array, op_array->opcodes[ssa_vars[j].definition].op2, ssa->rt_constants)) == IS_LONG) {
+                               ssa_var_info[j].use_as_double = 1;
+                       }
+                       ssa_var_info[j].type &= ~MAY_BE_ANY;
+                       zend_bitset_incl(worklist, j);
+               }
+       }
+       for (j = 0; j < op_array->last_var; j++) {
+               zend_bitset_excl(worklist, j);
+       }
+
+       if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) {
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
+static int is_recursive_tail_call(const zend_op_array *op_array,
+                                  zend_op             *opline)
+{
+       zend_func_info *info = ZEND_FUNC_INFO(op_array);
+
+       if (info->ssa.ops && info->ssa.vars &&
+           info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+           info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition >= 0) {
+
+               zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition;
+
+               if (op->opcode == ZEND_DO_UCALL) {
+                       zend_call_info *call_info = info->callee_info;
+
+                       while (call_info && call_info->caller_call_opline != op) {
+                               call_info = call_info->next_callee;
+                       }
+                       if (call_info && op_array == &call_info->callee_func->op_array) {
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
+void zend_func_return_info(const zend_op_array   *op_array,
+                           int                    recursive,
+                           int                    widening,
+                           zend_ssa_var_info     *ret)
+{
+       zend_func_info *info = ZEND_FUNC_INFO(op_array);
+       zend_ssa *ssa = &info->ssa;
+       int blocks_count = info->ssa.cfg.blocks_count;
+       zend_basic_block *blocks = info->ssa.cfg.blocks;
+       int j;
+       uint32_t t1;
+       uint32_t tmp = 0;
+       zend_class_entry *tmp_ce = NULL;
+       int tmp_is_instanceof = -1;
+       zend_class_entry *arg_ce;
+       int arg_is_instanceof;
+       zend_ssa_range tmp_range = {0, 0, 0, 0};
+       int tmp_has_range = -1;
+
+       if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
+               ret->type = MAY_BE_OBJECT | MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_RCN;
+               ret->ce = zend_ce_generator;
+               ret->is_instanceof = 0;
+               ret->range = tmp_range;
+               ret->has_range = 0;
+               return;
+       }
+
+       for (j = 0; j < blocks_count; j++) {
+               if (blocks[j].flags & ZEND_BB_REACHABLE) {
+                       zend_op *opline = op_array->opcodes + blocks[j].end;
+
+                       if (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF) {
+                               if (!recursive &&
+                                   info->ssa.ops &&
+                                   info->ssa.var_info &&
+                                   info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+                                   info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].recursive) {
+                                       continue;
+                               }
+                               if (is_recursive_tail_call(op_array, opline)) {
+                                       continue;
+                               }
+                               t1 = OP1_INFO();
+                               if (opline->opcode == ZEND_RETURN) {
+                                       t1 |= MAY_BE_DEF | MAY_BE_RC1 | MAY_BE_RCN;
+                                       t1 &= ~(MAY_BE_UNDEF | MAY_BE_REF);
+                               } else {
+                                       t1 |= MAY_BE_DEF | MAY_BE_REF;
+                                       t1 &= ~(MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN);
+                               }
+                               tmp |= t1;
+
+                               if (info->ssa.ops &&
+                                   info->ssa.var_info &&
+                                   info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+                                   info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].ce) {
+                                       arg_ce = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].ce;
+                                       arg_is_instanceof = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].is_instanceof;
+                               } else {
+                                       arg_ce = NULL;
+                                       arg_is_instanceof = 0;
+                               }
+
+                               if (tmp_is_instanceof < 0) {
+                                       tmp_ce = arg_ce;
+                                       tmp_is_instanceof = arg_is_instanceof;
+                               } else if (arg_ce && arg_ce == tmp_ce) {
+                                       if (tmp_is_instanceof != arg_is_instanceof) {
+                                               tmp_is_instanceof = 1;
+                                       }
+                               } else {
+                                       tmp_ce = NULL;
+                                       tmp_is_instanceof = 0;
+                               }
+
+                               if (opline->op1_type == IS_CONST) {
+                                       if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_NULL) {
+                                               if (tmp_has_range < 0) {
+                                                       tmp_has_range = 1;
+                                                       tmp_range.underflow = 0;
+                                                       tmp_range.min = 0;
+                                                       tmp_range.max = 0;
+                                                       tmp_range.overflow = 0;
+                                               } else if (tmp_has_range) {
+                                                       if (!tmp_range.underflow) {
+                                                               tmp_range.min = MIN(tmp_range.min, 0);
+                                                       }
+                                                       if (!tmp_range.overflow) {
+                                                               tmp_range.max = MAX(tmp_range.max, 0);
+                                                       }
+                                               }
+                                       } else if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_FALSE) {
+                                               if (tmp_has_range < 0) {
+                                                       tmp_has_range = 1;
+                                                       tmp_range.underflow = 0;
+                                                       tmp_range.min = 0;
+                                                       tmp_range.max = 0;
+                                                       tmp_range.overflow = 0;
+                                               } else if (tmp_has_range) {
+                                                       if (!tmp_range.underflow) {
+                                                               tmp_range.min = MIN(tmp_range.min, 0);
+                                                       }
+                                                       if (!tmp_range.overflow) {
+                                                               tmp_range.max = MAX(tmp_range.max, 0);
+                                                       }
+                                               }
+                                       } else if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_TRUE) {
+                                               if (tmp_has_range < 0) {
+                                                       tmp_has_range = 1;
+                                                       tmp_range.underflow = 0;
+                                                       tmp_range.min = 1;
+                                                       tmp_range.max = 1;
+                                                       tmp_range.overflow = 0;
+                                               } else if (tmp_has_range) {
+                                                       if (!tmp_range.underflow) {
+                                                               tmp_range.min = MIN(tmp_range.min, 1);
+                                                       }
+                                                       if (!tmp_range.overflow) {
+                                                               tmp_range.max = MAX(tmp_range.max, 1);
+                                                       }
+                                               }
+                                       } else if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_LONG) {
+                                               if (tmp_has_range < 0) {
+                                                       tmp_has_range = 1;
+                                                       tmp_range.underflow = 0;
+                                                       tmp_range.min = Z_LVAL_P(RT_CONSTANT(op_array, opline->op1));
+                                                       tmp_range.max = Z_LVAL_P(RT_CONSTANT(op_array, opline->op1));
+                                                       tmp_range.overflow = 0;
+                                               } else if (tmp_has_range) {
+                                                       if (!tmp_range.underflow) {
+                                                               tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(RT_CONSTANT(op_array, opline->op1)));
+                                                       }
+                                                       if (!tmp_range.overflow) {
+                                                               tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(RT_CONSTANT(op_array, opline->op1)));
+                                                       }
+                                               }
+                                       } else {
+                                               tmp_has_range = 0;
+                                       }
+                               } else if (info->ssa.ops &&
+                                          info->ssa.var_info &&
+                                          info->ssa.ops[opline - op_array->opcodes].op1_use >= 0) {
+                                       if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].has_range) {
+                                               if (tmp_has_range < 0) {
+                                                       tmp_has_range = 1;
+                                                       tmp_range = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range;
+                                               } else if (tmp_has_range) {
+                                                       /* union */
+                                                       if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.underflow) {
+                                                               tmp_range.underflow = 1;
+                                                               tmp_range.min = LONG_MIN;
+                                                       } else {
+                                                               tmp_range.min = MIN(tmp_range.min, info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.min);
+                                                       }
+                                                       if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.overflow) {
+                                                               tmp_range.overflow = 1;
+                                                               tmp_range.max = LONG_MAX;
+                                                       } else {
+                                                               tmp_range.max = MAX(tmp_range.max, info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.max);
+                                                       }
+                                               }
+                                       } else if (!widening) {
+                                               tmp_has_range = 1;
+                                               tmp_range.underflow = 1;
+                                               tmp_range.min = LONG_MIN;
+                                               tmp_range.max = LONG_MAX;
+                                               tmp_range.overflow = 1;
+                                       }
+                               } else {
+                                       tmp_has_range = 0;
+                               }
+                       }
+               }
+       }
+       if (tmp_is_instanceof < 0) {
+               tmp_is_instanceof = 0;
+               tmp_ce = NULL;
+       }
+       if (tmp_has_range < 0) {
+               tmp_has_range = 0;
+       }
+       ret->type = tmp;
+       ret->ce = tmp_ce;
+       ret->is_instanceof = tmp_is_instanceof;
+       ret->range = tmp_range;
+       ret->has_range = tmp_has_range;
+}
+
+static int zend_infer_types(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa)
+{
+       zend_ssa_var_info *ssa_var_info = ssa->var_info;
+       int ssa_vars_count = ssa->vars_count;
+       int j;
+       zend_bitset worklist;
+
+       worklist = alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+       memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+       /* Type Inference */
+       for (j = op_array->last_var; j < ssa_vars_count; j++) {
+               zend_bitset_incl(worklist, j);
+               ssa_var_info[j].type = 0;
+       }
+
+       if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) {
+               return FAILURE;
+       }
+
+       /* Narrowing integer initialization to doubles */
+       zend_type_narrowing(op_array, script, ssa);
+
+       if (ZEND_FUNC_INFO(op_array)) {
+               zend_func_return_info(op_array, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
+       }
+
+       return SUCCESS;
+}
+
+int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa) /* {{{ */
+{
+       zend_ssa_var_info *ssa_var_info;
+       int i;
+
+       if (!ssa->var_info) {
+               ssa->var_info = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var_info));
+       }
+       ssa_var_info = ssa->var_info;
+
+       for (i = 0; i < op_array->last_var; i++) {
+               if (!op_array->function_name) {
+                       ssa_var_info[i].type = 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;
+               } else if (i == EX_VAR_TO_NUM(op_array->this_var)) {
+                       ssa_var_info[i].type = MAY_BE_DEF | MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_OBJECT | MAY_BE_NULL;
+                       ssa_var_info[i].ce = op_array->scope;
+                       ssa_var_info[i].is_instanceof = 1;
+               } else {
+                       ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RCN | MAY_BE_NULL;
+               }
+               ssa_var_info[i].has_range = 0;
+       }
+       for (i = op_array->last_var; i < ssa->vars_count; i++) {
+               ssa_var_info[i].type = 0;
+               ssa_var_info[i].has_range = 0;
+       }
+
+       if (zend_infer_ranges(op_array, ssa) != SUCCESS) {
+               return FAILURE;
+       }
+
+       if (zend_infer_types(op_array, script, ssa) != SUCCESS) {
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+/* }}} */
+
+void zend_inference_check_recursive_dependencies(zend_op_array *op_array)
+{
+       zend_func_info *info = ZEND_FUNC_INFO(op_array);
+       zend_call_info *call_info;
+       zend_bitset worklist;
+       int worklist_len;
+
+       if (!info->ssa.var_info || !(info->flags & ZEND_FUNC_RECURSIVE)) {
+               return;
+       }
+       worklist_len = zend_bitset_len(info->ssa.vars_count);
+       worklist = alloca(sizeof(zend_ulong) * worklist_len);
+       memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+       call_info = info->callee_info;
+       while (call_info) {
+               if (call_info->recursive &&
+                   info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def >= 0) {
+                       zend_bitset_incl(worklist, info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def);
+               }
+               call_info = call_info->next_callee;
+       }
+       while (!zend_bitset_empty(worklist, worklist_len)) {
+               int i = zend_bitset_first(worklist, worklist_len);
+               zend_bitset_excl(worklist, i);
+               if (!info->ssa.var_info[i].recursive) {
+                       info->ssa.var_info[i].recursive = 1;
+                       add_usages(op_array, &info->ssa, worklist, i);
+               }
+       }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h
new file mode 100644 (file)
index 0000000..82f675c
--- /dev/null
@@ -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:
+ */
index fd7e3670376dc977de054b21ade937b58d5d2336..aeb2c42542f5450521a3984b38691a09a8eadac0 100644 (file)
@@ -27,6 +27,7 @@
 #include "zend_execute.h"
 #include "zend_vm.h"
 #include "zend_cfg.h"
+#include "zend_func_info.h"
 #include "zend_dump.h"
 
 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
@@ -549,7 +550,7 @@ static void zend_optimize(zend_op_array      *op_array,
        }
 
        if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
-               zend_dump_op_array(op_array, NULL, 1, "before optimizer");
+               zend_dump_op_array(op_array, 0, "before optimizer", NULL);
        }
 
        /* pass 1
@@ -561,7 +562,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
                zend_optimizer_pass1(op_array, ctx);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 1");
+                       zend_dump_op_array(op_array, 0, "after pass 1", NULL);
                }
        }
 
@@ -574,7 +575,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
                zend_optimizer_pass2(op_array);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 2");
+                       zend_dump_op_array(op_array, 0, "after pass 2", NULL);
                }
        }
 
@@ -586,7 +587,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
                zend_optimizer_pass3(op_array);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 1");
+                       zend_dump_op_array(op_array, 0, "after pass 1", NULL);
                }
        }
 
@@ -596,7 +597,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
                optimize_func_calls(op_array, ctx);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 1");
+                       zend_dump_op_array(op_array, 0, "after pass 1", NULL);
                }
        }
 
@@ -606,7 +607,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
                optimize_cfg(op_array, ctx);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 5");
+                       zend_dump_op_array(op_array, 0, "after pass 5", NULL);
                }
        }
 
@@ -616,7 +617,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) {
                optimize_dfa(op_array, ctx);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 6");
+                       zend_dump_op_array(op_array, 0, "after pass 6", NULL);
                }
        }
 
@@ -626,7 +627,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
                optimize_temporary_variables(op_array, ctx);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 9");
+                       zend_dump_op_array(op_array, 0, "after pass 9", NULL);
                }
        }
 
@@ -636,7 +637,7 @@ static void zend_optimize(zend_op_array      *op_array,
        if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
                zend_optimizer_nop_removal(op_array);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 10");
+                       zend_dump_op_array(op_array, 0, "after pass 10", NULL);
                }
        }
 
@@ -646,12 +647,12 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
                zend_optimizer_compact_literals(op_array, ctx);
                if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
-                       zend_dump_op_array(op_array, NULL, 1, "after pass 11");
+                       zend_dump_op_array(op_array, 0, "after pass 11", NULL);
                }
        }
 
        if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
-               zend_dump_op_array(op_array, NULL, 1, "after optimizer");
+               zend_dump_op_array(op_array, 0, "after optimizer", NULL);
        }
 }
 
@@ -794,3 +795,21 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend
 
        return 1;
 }
+
+int zend_optimizer_startup(void)
+{
+       return zend_func_info_startup();
+}
+
+int zend_optimizer_shutdown(void)
+{
+       return zend_func_info_shutdown();
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
index c991c37c1fbaf875a4572a812042ea4581e4c881..ad75ab3b7e6d23e5b5df97e2763e52df1ff1763c 100644 (file)
 #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
index 9ff82e977a3542ac1a24031440d4ff3f1c5a9ff4..5cdbd283ecd51878caa2f30b7417fd7833cbc822 100644 (file)
 #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
index 1e248e859783d30cdb419e0737a9a2e5787da502..dc0c1b3ababa68e0fc997434a3b69ddcc59251d5 100644 (file)
@@ -92,19 +92,58 @@ typedef struct _zend_ssa_var {
        unsigned int           scc_entry : 1;
 } zend_ssa_var;
 
+typedef struct _zend_ssa_var_info {
+       uint32_t               type; /* inferred type (see zend_inference.h) */
+       zend_ssa_range         range;
+       zend_class_entry      *ce;
+       unsigned int           has_range : 1;
+       unsigned int           is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
+       unsigned int           recursive : 1;
+       unsigned int           use_as_double : 1;
+} zend_ssa_var_info;
+
 typedef struct _zend_ssa {
+       zend_cfg               cfg;            /* control flow graph             */
+       int                    rt_constants;   /* run-time or compile-time       */
        int                    vars_count;     /* number of SSA variables        */
        zend_ssa_block        *blocks;         /* array of SSA blocks            */
        zend_ssa_op           *ops;            /* array of SSA instructions      */
        zend_ssa_var          *vars;           /* use/def chain of SSA variables */
+       int                    sccs;           /* number of SCCs                 */
+       zend_ssa_var_info     *var_info;
 } zend_ssa;
 
 BEGIN_EXTERN_C()
 
-int zend_build_ssa(zend_arena **arena, zend_op_array *op_array, zend_cfg *cfg, int rt_constants, zend_ssa *ssa, uint32_t *func_flags);
+int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags);
+int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
 
 END_EXTERN_C()
 
+static zend_always_inline int zend_ssa_next_use(zend_ssa_op *ssa_op, int var, int use)
+{
+       ssa_op += use;
+       if (ssa_op->result_use == var) {
+               return ssa_op->res_use_chain;
+       }
+       return (ssa_op->op1_use == var) ? ssa_op->op1_use_chain : ssa_op->op2_use_chain;
+}
+
+static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(zend_ssa *ssa, int var, zend_ssa_phi *p)
+{
+       if (p->pi >= 0) {
+               return p->use_chains[0];
+       } else {
+               int j;
+               for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+                       if (p->sources[j] == var) {
+                               return p->use_chains[j];
+                       }
+               }
+       }
+       return NULL;
+}
+
 #endif /* ZEND_SSA_H */
 
 /*
index a3ba341d64a74162e3b873f9a57a19fdc5b01ee2..c48e03992246da4460271742e20f460c5b33dff8 100644 (file)
@@ -1,8 +1,8 @@
 /*
    +----------------------------------------------------------------------+
-   | Zend OPcache JIT                                                     |
+   | Zend Engine                                                          |
    +----------------------------------------------------------------------+
-   | Copyright (c) 1998-2014 The PHP Group                                |
+   | Copyright (c) 1998-2015 The PHP Group                                |
    +----------------------------------------------------------------------+
    | This source file is subject to version 3.01 of the PHP license,      |
    | that is bundled with this package in the file LICENSE, and is        |
index 9c73b3bc616579391fe9e45f9ebde9494d8c07a8..770c8034c03c24a6bc8c4be002397316e71a7d0f 100644 (file)
@@ -407,6 +407,9 @@ fi
        Optimizer/zend_dfg.c \
        Optimizer/dfa_pass.c \
        Optimizer/zend_ssa.c \
+       Optimizer/zend_inference.c \
+       Optimizer/zend_func_info.c \
+       Optimizer/zend_call_graph.c \
        Optimizer/zend_dump.c,
        shared,,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1,,yes)
 
index 38cfb64be24ec232e9331cc0f8e15964696b7e33..35c464562040acb24986f553df38e7123a7effaa 100644 (file)
@@ -23,7 +23,7 @@ if (PHP_OPCACHE != "no") {
                zend_shared_alloc.c \
                shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
 
-       ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_dump.c", "opcache", "OptimizerObj");
+       ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c", "opcache", "OptimizerObj");
 
   
        ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname);