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