/* 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) {
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 */
#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
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
#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;
}
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 */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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:
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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:
+ */
#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) /* {{{ */
}
/* }}} */
-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;
}
/* }}} */
-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;
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;
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;
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;
zend_mark_reachable_blocks(op_array, cfg, 0);
if (func_flags) {
- *func_flags = flags;
+ *func_flags |= flags;
}
return SUCCESS;
}
/* }}} */
-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;
}
/* }}} */
-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;
#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 */
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()
#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;
}
} 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;
}
/* }}} */
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()
#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)
fprintf(stderr, " array(...)");
break;
default:
- fprintf(stderr, " ???");
+ fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
break;
}
}
}
}
-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)) {
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)) {
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)) {
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;
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) {
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) {
}
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);
}
}
-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++;
}
}
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) {
}
}
+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
#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()
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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:
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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:
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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:
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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:
+ */
#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)
}
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
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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:
+ */
#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;
} 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
#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) {
}
/* }}} */
-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;
}
/* }}} */
-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;
} 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);
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;
}
}
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;
}
}
/* }}} */
-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;
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;
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;
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;
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;
* 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 */
*/
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:
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;
}
}
}
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;
}
}
}
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;
}
}
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;
}
}
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) {
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;
}
}
-//???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));
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;
}
}
/* }}} */
+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
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 */
/*
/*
+----------------------------------------------------------------------+
- | 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 |
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)
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);