summaryrefslogtreecommitdiff
path: root/ext/opcache
diff options
context:
space:
mode:
Diffstat (limited to 'ext/opcache')
-rw-r--r--ext/opcache/Optimizer/block_pass.c1937
-rw-r--r--ext/opcache/Optimizer/compact_literals.c843
-rw-r--r--ext/opcache/Optimizer/compact_vars.c124
-rw-r--r--ext/opcache/Optimizer/dce.c610
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c1637
-rw-r--r--ext/opcache/Optimizer/escape_analysis.c539
-rw-r--r--ext/opcache/Optimizer/nop_removal.c106
-rw-r--r--ext/opcache/Optimizer/optimize_func_calls.c337
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c187
-rw-r--r--ext/opcache/Optimizer/pass1.c685
-rw-r--r--ext/opcache/Optimizer/pass3.c356
-rw-r--r--ext/opcache/Optimizer/sccp.c2524
-rw-r--r--ext/opcache/Optimizer/scdf.c230
-rw-r--r--ext/opcache/Optimizer/scdf.h99
-rw-r--r--ext/opcache/Optimizer/ssa_integrity.c379
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.c271
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.h69
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c909
-rw-r--r--ext/opcache/Optimizer/zend_cfg.h127
-rw-r--r--ext/opcache/Optimizer/zend_dfg.c333
-rw-r--r--ext/opcache/Optimizer/zend_dfg.h51
-rw-r--r--ext/opcache/Optimizer/zend_dump.c1239
-rw-r--r--ext/opcache/Optimizer/zend_dump.h48
-rw-r--r--ext/opcache/Optimizer/zend_func_info.c974
-rw-r--r--ext/opcache/Optimizer/zend_func_info.h68
-rw-r--r--ext/opcache/Optimizer/zend_inference.c4680
-rw-r--r--ext/opcache/Optimizer/zend_inference.h292
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c1568
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.h96
-rw-r--r--ext/opcache/Optimizer/zend_optimizer_internal.h123
-rw-r--r--ext/opcache/Optimizer/zend_ssa.c1628
-rw-r--r--ext/opcache/Optimizer/zend_ssa.h326
-rw-r--r--ext/opcache/Optimizer/zend_worklist.h121
-rw-r--r--ext/opcache/ZendAccelerator.c83
-rw-r--r--ext/opcache/ZendAccelerator.h65
-rw-r--r--ext/opcache/config.m422
-rw-r--r--ext/opcache/config.w322
-rw-r--r--ext/opcache/jit/zend_jit.c30
-rw-r--r--ext/opcache/jit/zend_jit.h8
-rw-r--r--ext/opcache/jit/zend_jit_helpers.c119
-rw-r--r--ext/opcache/jit/zend_jit_internal.h6
-rw-r--r--ext/opcache/jit/zend_jit_trace.c52
-rw-r--r--ext/opcache/jit/zend_jit_vm_helpers.c2
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc279
-rw-r--r--ext/opcache/jit/zend_jit_x86.h2
-rw-r--r--ext/opcache/shared_alloc_mmap.c22
-rw-r--r--ext/opcache/shared_alloc_win32.c10
-rw-r--r--ext/opcache/tests/bug65915.phpt6
-rw-r--r--ext/opcache/tests/bug76337.phpt2
-rw-r--r--ext/opcache/tests/bug78961.phpt17
-rw-r--r--ext/opcache/tests/internal_func_info_static_method.phpt2
-rw-r--r--ext/opcache/tests/jit/bug80426.phpt2
-rw-r--r--ext/opcache/tests/opt/dce_009.phpt71
-rw-r--r--ext/opcache/tests/zzz_basic_logging.phpt6
-rw-r--r--ext/opcache/zend_accelerator_blacklist.c12
-rw-r--r--ext/opcache/zend_accelerator_blacklist.h2
-rw-r--r--ext/opcache/zend_accelerator_debug.c24
-rw-r--r--ext/opcache/zend_accelerator_debug.h1
-rw-r--r--ext/opcache/zend_accelerator_hash.c6
-rw-r--r--ext/opcache/zend_accelerator_hash.h6
-rw-r--r--ext/opcache/zend_accelerator_module.c6
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c124
-rw-r--r--ext/opcache/zend_persist.c9
-rw-r--r--ext/opcache/zend_persist_calc.c8
-rw-r--r--ext/opcache/zend_shared_alloc.c22
-rw-r--r--ext/opcache/zend_shared_alloc.h2
66 files changed, 432 insertions, 24114 deletions
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
deleted file mode 100644
index bad814ed6e..0000000000
--- a/ext/opcache/Optimizer/block_pass.c
+++ /dev/null
@@ -1,1937 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-#include "zend_bitset.h"
-#include "zend_cfg.h"
-#include "zend_dump.h"
-
-/* Checks if a constant (like "true") may be replaced by its value */
-int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
-{
- zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
- if (c) {
- if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
- && !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
- && (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
- || !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
- ZVAL_COPY_VALUE(result, &c->value);
- if (copy) {
- Z_TRY_ADDREF_P(result);
- }
- return 1;
- } else {
- return 0;
- }
- }
-
- /* Special constants null/true/false can always be substituted. */
- c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
- if (c) {
- ZVAL_COPY_VALUE(result, &c->value);
- return 1;
- }
- return 0;
-}
-
-/* Data dependencies macros */
-
-#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
-#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
-
-static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
-{
- zend_op *opcodes = op_array->opcodes;
-
- do {
- b->start++;
- b->len--;
- } while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP);
-}
-
-static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
-{
- uint32_t i, j;
-
- if (b->len == 0) {
- return;
- }
-
- if (op_array->opcodes[b->start].opcode == ZEND_NOP) {
- strip_leading_nops(op_array, b);
- }
-
- if (b->len == 0) {
- return;
- }
-
- /* strip the inside NOPs */
- i = j = b->start + 1;
- while (i < b->start + b->len) {
- if (op_array->opcodes[i].opcode != ZEND_NOP) {
- if (i != j) {
- op_array->opcodes[j] = op_array->opcodes[i];
- }
- j++;
- }
- i++;
- }
- b->len = j - b->start;
- while (j < i) {
- MAKE_NOP(op_array->opcodes + j);
- j++;
- }
-}
-
-static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) {
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
- zval *zv;
- if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG)
- || (opline->opcode == ZEND_SWITCH_STRING && Z_TYPE_P(val) != IS_STRING)) {
- /* fallback to next block */
- return block->successors[block->successors_count - 1];
- }
- if (opline->opcode == ZEND_MATCH && Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_STRING) {
- /* always jump to the default arm */
- return block->successors[block->successors_count - 1];
- }
- if (Z_TYPE_P(val) == IS_LONG) {
- zv = zend_hash_index_find(jumptable, Z_LVAL_P(val));
- } else {
- ZEND_ASSERT(Z_TYPE_P(val) == IS_STRING);
- zv = zend_hash_find(jumptable, Z_STR_P(val));
- }
- if (!zv) {
- /* default */
- return block->successors[block->successors_count - (opline->opcode == ZEND_MATCH ? 1 : 2)];
- }
- return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
-}
-
-static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource, uint32_t *opt_count)
-{
- zend_op *opline, *src;
- zend_op *end, *last_op = NULL;
-
- if (block->len == 0) {
- return;
- }
-
- if (op_array->opcodes[block->start].opcode == ZEND_NOP) {
- /* remove leading NOPs */
- strip_leading_nops(op_array, block);
- }
-
- opline = op_array->opcodes + block->start;
- end = opline + block->len;
- while (opline < end) {
- /* Constant Propagation: strip X = QM_ASSIGN(const) */
- if (opline->op1_type == IS_TMP_VAR &&
- opline->opcode != ZEND_FREE) {
- src = VAR_SOURCE(opline->op1);
- if (src &&
- src->opcode == ZEND_QM_ASSIGN &&
- src->op1_type == IS_CONST
- ) {
- znode_op op1 = opline->op1;
- if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
- COPY_NODE(opline->result, opline->op1);
- COPY_NODE(opline->op1, src->op1);
- VAR_SOURCE(op1) = NULL;
- MAKE_NOP(src);
- ++(*opt_count);
- } else {
- zval c;
- ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
- if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
- VAR_SOURCE(op1) = NULL;
- literal_dtor(&ZEND_OP1_LITERAL(src));
- MAKE_NOP(src);
- ++(*opt_count);
- } else {
- zval_ptr_dtor_nogc(&c);
- }
- }
- }
- }
-
- /* Constant Propagation: strip X = QM_ASSIGN(const) */
- if (opline->op2_type == IS_TMP_VAR) {
- src = VAR_SOURCE(opline->op2);
- if (src &&
- src->opcode == ZEND_QM_ASSIGN &&
- src->op1_type == IS_CONST) {
-
- znode_op op2 = opline->op2;
- zval c;
-
- ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
- if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
- VAR_SOURCE(op2) = NULL;
- literal_dtor(&ZEND_OP1_LITERAL(src));
- MAKE_NOP(src);
- ++(*opt_count);
- } else {
- zval_ptr_dtor_nogc(&c);
- }
- }
- }
-
- switch (opline->opcode) {
- case ZEND_ECHO:
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- src = VAR_SOURCE(opline->op1);
- if (src &&
- src->opcode == ZEND_CAST &&
- src->extended_value == IS_STRING) {
- /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- }
- } else if (opline->op1_type == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE) {
- if (last_op == opline - 1) {
- /* compress consecutive ECHO's.
- * Float to string conversion may be affected by current
- * locale setting.
- */
- int l, old_len;
-
- if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
- convert_to_string(&ZEND_OP1_LITERAL(opline));
- }
- if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
- convert_to_string(&ZEND_OP1_LITERAL(last_op));
- }
- old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
- l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
- if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
- zend_string *tmp = zend_string_alloc(l, 0);
- memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
- Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
- } else {
- Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
- }
- Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
- memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
- Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
- zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
- ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
- ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
- MAKE_NOP(last_op);
- ++(*opt_count);
- }
- last_op = opline;
- }
- break;
-
- case ZEND_FREE:
- if (opline->op1_type == IS_TMP_VAR) {
- src = VAR_SOURCE(opline->op1);
- if (src) {
- switch (src->opcode) {
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- /* T = BOOL(X), FREE(T) => T = BOOL(X) */
- /* The remaining BOOL is removed by a separate optimization */
- VAR_SOURCE(opline->op1) = NULL;
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_PRE_INC_STATIC_PROP:
- case ZEND_PRE_DEC_STATIC_PROP:
- src->result_type = IS_UNUSED;
- VAR_SOURCE(opline->op1) = NULL;
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- default:
- break;
- }
- }
- } else if (opline->op1_type == IS_VAR) {
- src = VAR_SOURCE(opline->op1);
- /* V = OP, FREE(V) => OP. NOP */
- if (src &&
- src->opcode != ZEND_FETCH_R &&
- src->opcode != ZEND_FETCH_STATIC_PROP_R &&
- src->opcode != ZEND_FETCH_DIM_R &&
- src->opcode != ZEND_FETCH_OBJ_R &&
- src->opcode != ZEND_NEW) {
- src->result_type = IS_UNUSED;
- MAKE_NOP(opline);
- ++(*opt_count);
- if (src->opcode == ZEND_QM_ASSIGN) {
- if (src->op1_type & (IS_VAR|IS_TMP_VAR)) {
- src->opcode = ZEND_FREE;
- } else {
- MAKE_NOP(src);
- }
- }
- }
- }
- break;
-
-#if 0
- /* pre-evaluate functions:
- constant(x)
- function_exists(x)
- extension_loaded(x)
- BAD: interacts badly with Accelerator
- */
- if((opline->op1_type & IS_VAR) &&
- VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
- VAR_SOURCE(opline->op1)->extended_value == 1) {
- zend_op *fcall = VAR_SOURCE(opline->op1);
- zend_op *sv = fcall-1;
- if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
- sv->op1_type == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
- Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
- ) {
- zval *arg = &OPLINE_OP1_LITERAL(sv);
- char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
- int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
- if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
- (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
- ) {
- zend_function *function;
- if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) {
- literal_dtor(arg);
- MAKE_NOP(sv);
- MAKE_NOP(fcall);
- LITERAL_BOOL(opline->op1, 1);
- opline->op1_type = IS_CONST;
- }
- } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
- zval c;
- if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 1 ELS_CC) != 0) {
- literal_dtor(arg);
- MAKE_NOP(sv);
- MAKE_NOP(fcall);
- ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c);
- /* no copy ctor - get already copied it */
- opline->op1_type = IS_CONST;
- }
- } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
- if(zend_hash_exists(&module_registry, Z_STR_P(arg))) {
- literal_dtor(arg);
- MAKE_NOP(sv);
- MAKE_NOP(fcall);
- LITERAL_BOOL(opline->op1, 1);
- opline->op1_type = IS_CONST;
- }
- }
- }
- }
-#endif
-
- case ZEND_FETCH_LIST_R:
- case ZEND_FETCH_LIST_W:
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- /* LIST variable will be deleted later by FREE */
- Tsource[VAR_NUM(opline->op1.var)] = NULL;
- }
- break;
-
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- /* SWITCH variable will be deleted later by FREE, so we can't optimize it */
- Tsource[VAR_NUM(opline->op1.var)] = NULL;
- break;
- }
- if (opline->op1_type == IS_CONST) {
- int target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- opline->opcode = ZEND_JMP;
- opline->op1_type = IS_UNUSED;
- opline->op2_type = IS_UNUSED;
- block->successors_count = 1;
- block->successors[0] = target;
- }
- break;
-
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- case ZEND_COPY_TMP:
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- /* Variable will be deleted later by FREE, so we can't optimize it */
- Tsource[VAR_NUM(opline->op1.var)] = NULL;
- break;
- }
- /* break missing intentionally */
-
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- if (opline->op1_type == IS_CONST &&
- opline->op2_type == IS_CONST) {
- goto optimize_constant_binary_op;
- }
- /* IS_EQ(TRUE, X) => BOOL(X)
- * IS_EQ(FALSE, X) => BOOL_NOT(X)
- * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
- * IS_NOT_EQ(FALSE, X) => BOOL(X)
- * CASE(TRUE, X) => BOOL(X)
- * CASE(FALSE, X) => BOOL_NOT(X)
- */
- if (opline->op1_type == IS_CONST &&
- (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
- /* Optimization of comparison with "null" is not safe,
- * because ("0" == null) is not equal to !("0")
- */
- opline->opcode =
- ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
- ZEND_BOOL : ZEND_BOOL_NOT;
- COPY_NODE(opline->op1, opline->op2);
- SET_UNUSED(opline->op2);
- ++(*opt_count);
- goto optimize_bool;
- } else if (opline->op2_type == IS_CONST &&
- (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
- Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
- /* Optimization of comparison with "null" is not safe,
- * because ("0" == null) is not equal to !("0")
- */
- opline->opcode =
- ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
- ZEND_BOOL : ZEND_BOOL_NOT;
- SET_UNUSED(opline->op2);
- ++(*opt_count);
- goto optimize_bool;
- }
- break;
-
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- optimize_bool:
- if (opline->op1_type == IS_CONST) {
- goto optimize_const_unary_op;
- }
- if (opline->op1_type == IS_TMP_VAR &&
- !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
- src = VAR_SOURCE(opline->op1);
- if (src) {
- switch (src->opcode) {
- case ZEND_BOOL_NOT:
- /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
- MAKE_NOP(src);
- ++(*opt_count);
- goto optimize_bool;
- case ZEND_BOOL:
- /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- goto optimize_bool;
- case ZEND_IS_EQUAL:
- if (opline->opcode == ZEND_BOOL_NOT) {
- src->opcode = ZEND_IS_NOT_EQUAL;
- }
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- case ZEND_IS_NOT_EQUAL:
- if (opline->opcode == ZEND_BOOL_NOT) {
- src->opcode = ZEND_IS_EQUAL;
- }
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- case ZEND_IS_IDENTICAL:
- if (opline->opcode == ZEND_BOOL_NOT) {
- src->opcode = ZEND_IS_NOT_IDENTICAL;
- }
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- case ZEND_IS_NOT_IDENTICAL:
- if (opline->opcode == ZEND_BOOL_NOT) {
- src->opcode = ZEND_IS_IDENTICAL;
- }
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- case ZEND_IS_SMALLER:
- if (opline->opcode == ZEND_BOOL_NOT) {
- zend_uchar tmp_type;
- uint32_t tmp;
-
- src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
- tmp_type = src->op1_type;
- src->op1_type = src->op2_type;
- src->op2_type = tmp_type;
- tmp = src->op1.num;
- src->op1.num = src->op2.num;
- src->op2.num = tmp;
- }
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- case ZEND_IS_SMALLER_OR_EQUAL:
- if (opline->opcode == ZEND_BOOL_NOT) {
- zend_uchar tmp_type;
- uint32_t tmp;
-
- src->opcode = ZEND_IS_SMALLER;
- tmp_type = src->op1_type;
- src->op1_type = src->op2_type;
- src->op2_type = tmp_type;
- tmp = src->op1.num;
- src->op1.num = src->op2.num;
- src->op2.num = tmp;
- }
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- case ZEND_ISSET_ISEMPTY_CV:
- 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_INSTANCEOF:
- case ZEND_TYPE_CHECK:
- case ZEND_DEFINED:
- case ZEND_IN_ARRAY:
- case ZEND_ARRAY_KEY_EXISTS:
- if (opline->opcode == ZEND_BOOL_NOT) {
- break;
- }
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
- ++(*opt_count);
- break;
- }
- }
- }
- break;
-
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- while (1) {
- if (opline->op1_type == IS_CONST) {
- ++(*opt_count);
- block->successors_count = 1;
- if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
- (opline->opcode == ZEND_JMPZ)) {
-
- MAKE_NOP(opline);
- block->successors[0] = block->successors[1];
- block->len--;
- cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
- break;
- } else {
- zend_basic_block *next = cfg->blocks + block->successors[1];
-
- next->flags &= ~ZEND_BB_FOLLOW;
- if (!(next->flags & (ZEND_BB_TARGET|ZEND_BB_PROTECTED))) {
- next->flags &= ~ZEND_BB_REACHABLE;
- }
- opline->opcode = ZEND_JMP;
- COPY_NODE(opline->op1, opline->op2);
- break;
- }
- } else if (opline->op1_type == IS_TMP_VAR &&
- !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
- src = VAR_SOURCE(opline->op1);
- if (src) {
- if (src->opcode == ZEND_BOOL_NOT) {
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
- opline->opcode = INV_COND(opline->opcode);
- MAKE_NOP(src);
- ++(*opt_count);
- continue;
- } else if (src->opcode == ZEND_BOOL ||
- src->opcode == ZEND_QM_ASSIGN) {
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- continue;
- }
- }
- }
- break;
- }
- break;
-
- case ZEND_JMPZNZ:
- while (1) {
- if (opline->op1_type == IS_CONST) {
- ++(*opt_count);
- if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
- zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
- block->successors[0] = block->successors[1];
- } else {
- zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
- }
- block->successors_count = 1;
- opline->op1_type = IS_UNUSED;
- opline->extended_value = 0;
- opline->opcode = ZEND_JMP;
- break;
- } else if (opline->op1_type == IS_TMP_VAR &&
- !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
- src = VAR_SOURCE(opline->op1);
- if (src) {
- if (src->opcode == ZEND_BOOL_NOT) {
- /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
- uint32_t tmp;
-
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- tmp = block->successors[0];
- block->successors[0] = block->successors[1];
- block->successors[1] = tmp;
- MAKE_NOP(src);
- ++(*opt_count);
- continue;
- } else if (src->opcode == ZEND_BOOL ||
- src->opcode == ZEND_QM_ASSIGN) {
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- continue;
- }
- }
- }
- break;
- }
- break;
-
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- while (1) {
- if (opline->op1_type == IS_CONST) {
- if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
- (opline->opcode == ZEND_JMPZ_EX)) {
-
- ++(*opt_count);
- opline->opcode = ZEND_QM_ASSIGN;
- zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
- ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), opline->opcode == ZEND_JMPZ_EX);
- opline->op2.num = 0;
- block->successors_count = 1;
- block->successors[0] = block->successors[1];
- cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
- break;
- }
- } else if (opline->op1_type == IS_TMP_VAR &&
- (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
- opline->result.var == opline->op1.var)) {
- src = VAR_SOURCE(opline->op1);
- if (src) {
- if (src->opcode == ZEND_BOOL ||
- src->opcode == ZEND_QM_ASSIGN) {
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- continue;
- }
- }
- }
- break;
- }
- break;
-
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- if (opline->op1_type == IS_CONST &&
- opline->op2_type == IS_CONST) {
- goto optimize_constant_binary_op;
- }
-
- if (opline->op2_type == IS_CONST &&
- opline->op1_type == IS_TMP_VAR) {
-
- src = VAR_SOURCE(opline->op1);
- if (src &&
- (src->opcode == ZEND_CONCAT ||
- src->opcode == ZEND_FAST_CONCAT) &&
- src->op2_type == IS_CONST) {
- /* compress consecutive CONCATs */
- int l, old_len;
-
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
- convert_to_string(&ZEND_OP2_LITERAL(opline));
- }
- if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
- convert_to_string(&ZEND_OP2_LITERAL(src));
- }
-
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
- l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
- if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
- zend_string *tmp = zend_string_alloc(l, 0);
- memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
- Z_STR(ZEND_OP2_LITERAL(src)) = tmp;
- } else {
- Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
- }
- Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX;
- memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
- Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
- zval_ptr_dtor_str(&ZEND_OP2_LITERAL(opline));
- ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
- ZVAL_NULL(&ZEND_OP2_LITERAL(src));
- MAKE_NOP(src);
- ++(*opt_count);
- }
- }
-
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- src = VAR_SOURCE(opline->op1);
- if (src &&
- src->opcode == ZEND_CAST &&
- src->extended_value == IS_STRING &&
- src->op1_type != IS_CONST) {
- /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- }
- }
- if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
- src = VAR_SOURCE(opline->op2);
- if (src &&
- src->opcode == ZEND_CAST &&
- src->extended_value == IS_STRING &&
- src->op1_type != IS_CONST) {
- /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
- zend_op *src = VAR_SOURCE(opline->op2);
- VAR_SOURCE(opline->op2) = NULL;
- COPY_NODE(opline->op2, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- }
- }
- if (opline->op1_type == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
- Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
- /* convert CONCAT('', X) => CAST(STRING, X) */
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- opline->opcode = ZEND_CAST;
- opline->extended_value = IS_STRING;
- COPY_NODE(opline->op1, opline->op2);
- opline->op2_type = IS_UNUSED;
- opline->op2.var = 0;
- ++(*opt_count);
- } else if (opline->op2_type == IS_CONST &&
- Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
- Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
- /* convert CONCAT(X, '') => CAST(STRING, X) */
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- opline->opcode = ZEND_CAST;
- opline->extended_value = IS_STRING;
- opline->op2_type = IS_UNUSED;
- opline->op2.var = 0;
- ++(*opt_count);
- } else if (opline->opcode == ZEND_CONCAT &&
- (opline->op1_type == IS_CONST ||
- (opline->op1_type == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) &&
- (opline->op2_type == IS_CONST ||
- (opline->op2_type == IS_TMP_VAR &&
- VAR_SOURCE(opline->op2) &&
- (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
- VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
- VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
- VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
- opline->opcode = ZEND_FAST_CONCAT;
- ++(*opt_count);
- }
- break;
-
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- case ZEND_DIV:
- case ZEND_MOD:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_BOOL_XOR:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- if (opline->op1_type == IS_CONST &&
- opline->op2_type == IS_CONST) {
- /* evaluate constant expressions */
- zval result;
-
-optimize_constant_binary_op:
- if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- opline->opcode = ZEND_QM_ASSIGN;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &result);
- ++(*opt_count);
- }
- }
- break;
-
- case ZEND_BW_NOT:
- if (opline->op1_type == IS_CONST) {
- /* evaluate constant unary ops */
- zval result;
-
-optimize_const_unary_op:
- if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- opline->opcode = ZEND_QM_ASSIGN;
- zend_optimizer_update_op1_const(op_array, opline, &result);
- ++(*opt_count);
- }
- }
- break;
-
- case ZEND_CAST:
- if (opline->op1_type == IS_CONST) {
- /* cast of constant operand */
- zval result;
-
- if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- zend_optimizer_update_op1_const(op_array, opline, &result);
- ++(*opt_count);
- }
- }
- break;
-
- case ZEND_STRLEN:
- if (opline->op1_type == IS_CONST) {
- zval result;
-
- if (zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- opline->opcode = ZEND_QM_ASSIGN;
- zend_optimizer_update_op1_const(op_array, opline, &result);
- ++(*opt_count);
- }
- }
- break;
-
- case ZEND_RETURN:
- case ZEND_EXIT:
- if (opline->op1_type == IS_TMP_VAR) {
- src = VAR_SOURCE(opline->op1);
- if (src && src->opcode == ZEND_QM_ASSIGN) {
- zend_op *op = src + 1;
- zend_bool optimize = 1;
-
- while (op < opline) {
- if ((op->op1_type == opline->op1_type
- && op->op1.var == opline->op1.var)
- || (op->op2_type == opline->op1_type
- && op->op2.var == opline->op1.var)) {
- optimize = 0;
- break;
- }
- op++;
- }
-
- if (optimize) {
- /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */
- VAR_SOURCE(opline->op1) = NULL;
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- ++(*opt_count);
- }
- }
- }
- break;
-
- case ZEND_QM_ASSIGN:
- if (opline->op1_type == opline->result_type &&
- opline->op1.var == opline->result.var) {
- /* strip T = QM_ASSIGN(T) */
- MAKE_NOP(opline);
- ++(*opt_count);
- } else if (opline->op1_type == IS_TMP_VAR &&
- opline->result_type == IS_TMP_VAR &&
- !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
- /* T1 = ..., T2 = QM_ASSIGN(T1) to T2 = ..., NOP */
- src = VAR_SOURCE(opline->op1);
- if (src &&
- src->opcode != ZEND_COPY_TMP &&
- src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
- src->opcode != ZEND_ADD_ARRAY_UNPACK &&
- (src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
- src == opline -1)) {
- src->result.var = opline->result.var;
- VAR_SOURCE(opline->op1) = NULL;
- VAR_SOURCE(opline->result) = src;
- MAKE_NOP(opline);
- ++(*opt_count);
- }
- }
- break;
- }
-
- /* get variable source */
- if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
- SET_VAR_SOURCE(opline);
- }
- opline++;
- }
-}
-
-/* Rebuild plain (optimized) op_array from CFG */
-static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_basic_block *blocks = cfg->blocks;
- zend_basic_block *end = blocks + cfg->blocks_count;
- zend_basic_block *b;
- zend_op *new_opcodes;
- zend_op *opline;
- uint32_t len = 0;
- int n;
-
- for (b = blocks; b < end; b++) {
- if (b->len == 0) {
- continue;
- }
- if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
- if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
- /* Only keep the FREE for the loop var */
- ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
- || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
- len += b->len = 1;
- continue;
- }
-
- opline = op_array->opcodes + b->start + b->len - 1;
- if (opline->opcode == ZEND_JMP) {
- zend_basic_block *next = b + 1;
-
- while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
- next++;
- }
- if (next < end && next == blocks + b->successors[0]) {
- /* JMP to the next block - strip it */
- MAKE_NOP(opline);
- b->len--;
- }
- } else if (b->len == 1 && opline->opcode == ZEND_NOP) {
- /* skip empty block */
- b->len--;
- }
- len += b->len;
- } else {
- /* this block will not be used, delete all constants there */
- zend_op *op = op_array->opcodes + b->start;
- zend_op *end = op + b->len;
- for (; op < end; op++) {
- if (op->op1_type == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(op));
- }
- if (op->op2_type == IS_CONST) {
- literal_dtor(&ZEND_OP2_LITERAL(op));
- }
- }
- }
- }
-
- new_opcodes = emalloc(len * sizeof(zend_op));
- opline = new_opcodes;
-
- /* Copy code of reachable blocks into a single buffer */
- for (b = blocks; b < end; b++) {
- if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
- memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
- b->start = opline - new_opcodes;
- opline += b->len;
- }
- }
-
- /* adjust jump targets */
- efree(op_array->opcodes);
- op_array->opcodes = new_opcodes;
- op_array->last = len;
-
- for (b = blocks; b < end; b++) {
- if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) {
- continue;
- }
- opline = op_array->opcodes + b->start + b->len - 1;
- switch (opline->opcode) {
- case ZEND_FAST_CALL:
- case ZEND_JMP:
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start);
- break;
- case ZEND_JMPZNZ:
- opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[1]].start);
- /* break missing intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
- break;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- {
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
- zval *zv;
- uint32_t s = 0;
- ZEND_ASSERT(b->successors_count == (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable));
-
- ZEND_HASH_FOREACH_VAL(jumptable, zv) {
- Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
- } ZEND_HASH_FOREACH_END();
- opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
- break;
- }
- }
- }
-
- /* adjust exception jump targets & remove unused try_catch_array entries */
- if (op_array->last_try_catch) {
- int i, j;
- uint32_t *map;
- ALLOCA_FLAG(use_heap);
-
- map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap);
- for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
- if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) {
- map[i] = j;
- op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start;
- if (op_array->try_catch_array[i].catch_op) {
- op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start;
- } else {
- op_array->try_catch_array[j].catch_op = 0;
- }
- if (op_array->try_catch_array[i].finally_op) {
- op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start;
- } else {
- op_array->try_catch_array[j].finally_op = 0;
- }
- if (!op_array->try_catch_array[i].finally_end) {
- op_array->try_catch_array[j].finally_end = 0;
- } else {
- op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start;
- }
- j++;
- }
- }
- if (i != j) {
- op_array->last_try_catch = j;
- if (j == 0) {
- efree(op_array->try_catch_array);
- op_array->try_catch_array = NULL;
- }
-
- if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
- zend_op *opline = new_opcodes;
- zend_op *end = opline + len;
- while (opline < end) {
- if (opline->opcode == ZEND_FAST_RET &&
- opline->op2.num != (uint32_t)-1 &&
- opline->op2.num < (uint32_t)j) {
- opline->op2.num = map[opline->op2.num];
- }
- opline++;
- }
- }
- }
- free_alloca(map, use_heap);
- }
-
- /* adjust early binding list */
- if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
- ZEND_ASSERT(op_array == &ctx->script->main_op_array);
- ctx->script->first_early_binding_opline =
- zend_build_delayed_early_binding_list(op_array);
- }
-
- /* rebuild map (just for printing) */
- memset(cfg->map, -1, sizeof(int) * op_array->last);
- for (n = 0; n < cfg->blocks_count; n++) {
- if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
- cfg->map[cfg->blocks[n].start] = n;
- }
- }
-}
-
-static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
-{
- int b;
- zend_basic_block *target_block = cfg->blocks + block->successors[n];
-
- if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
- do {
- b = target_block->successors[0];
- target_block = cfg->blocks + b;
- } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
- block->successors[n] = b;
- ++(*opt_count);
- }
- return target_block;
-}
-
-static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
-{
- int b;
- zend_basic_block *target_block = cfg->blocks + block->successors[n];
-
- if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
- do {
- b = target_block->successors[0];
- target_block = cfg->blocks + b;
- } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
- block->successors[n] = b;
- ++(*opt_count);
- }
- return target_block;
-}
-
-static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg, zend_basic_block *block)
-{
- zend_basic_block *next_block = block + 1;
- zend_basic_block *end = cfg->blocks + cfg->blocks_count;
-
- while (1) {
- if (next_block == end) {
- return NULL;
- } else if (next_block->flags & ZEND_BB_REACHABLE) {
- break;
- }
- next_block++;
- }
- while (next_block->len == 0 && !(next_block->flags & ZEND_BB_PROTECTED)) {
- next_block = cfg->blocks + next_block->successors[0];
- }
- return next_block;
-}
-
-
-/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
-static zend_always_inline int in_hitlist(int target, int *jmp_hitlist, int jmp_hitlist_count)
-{
- int i;
-
- for (i = 0; i < jmp_hitlist_count; i++) {
- if (jmp_hitlist[i] == target) {
- return 1;
- }
- }
- return 0;
-}
-
-#define CHECK_LOOP(target) \
- if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
- jmp_hitlist[jmp_hitlist_count++] = target; \
- } else { \
- break; \
- }
-
-static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, const zend_cfg *cfg, int *jmp_hitlist, uint32_t *opt_count)
-{
- /* last_op is the last opcode of the current block */
- zend_basic_block *target_block, *follow_block, *next_block;
- zend_op *last_op, *target;
- int next, jmp_hitlist_count;
-
- if (block->len == 0) {
- return;
- }
-
- last_op = op_array->opcodes + block->start + block->len - 1;
- switch (last_op->opcode) {
- case ZEND_JMP:
- jmp_hitlist_count = 0;
-
- target_block = get_target_block(cfg, block, 0, opt_count);
- while (target_block->len == 1) {
- target = op_array->opcodes + target_block->start;
- if (target->opcode == ZEND_JMP) {
- /* JMP L, L: JMP L1 -> JMP L1 */
- next = target_block->successors[0];
- } else {
- break;
- }
- CHECK_LOOP(next);
- block->successors[0] = next;
- ++(*opt_count);
- target_block = get_target_block(cfg, block, 0, opt_count);
- }
-
- next_block = get_next_block(cfg, block);
- if (target_block == next_block) {
- /* JMP(next) -> NOP */
- MAKE_NOP(last_op);
- ++(*opt_count);
- block->len--;
- } else if (target_block->len == 1) {
- target = op_array->opcodes + target_block->start;
- if (target->opcode == ZEND_JMPZNZ) {
- /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
- *last_op = *target;
- if (last_op->op1_type == IS_CONST) {
- zval zv;
- ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
- last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
- }
- block->successors_count = 2;
- block->successors[0] = target_block->successors[0];
- block->successors[1] = target_block->successors[1];
- ++(*opt_count);
- goto optimize_jmpznz;
- } else if ((target->opcode == ZEND_RETURN ||
- target->opcode == ZEND_RETURN_BY_REF ||
- target->opcode == ZEND_GENERATOR_RETURN ||
- target->opcode == ZEND_EXIT) &&
- !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
- /* JMP L, L: RETURN to immediate RETURN */
- *last_op = *target;
- if (last_op->op1_type == IS_CONST) {
- zval zv;
- ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
- last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
- }
- block->successors_count = 0;
- ++(*opt_count);
- }
- }
- break;
-
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_JMP_NULL:
- jmp_hitlist_count = 0;
-
- target_block = get_target_block(cfg, block, 0, opt_count);
- while (target_block->len == 1) {
- target = op_array->opcodes + target_block->start;
-
- if (target->opcode == ZEND_JMP) {
- /* JMP_SET(X, L), L: JMP(L2) -> JMP_SET(X, L2) */
- next = target_block->successors[0];
- CHECK_LOOP(next);
- block->successors[0] = next;
- ++(*opt_count);
- } else {
- break;
- }
- target_block = get_target_block(cfg, block, 0, opt_count);
- }
- break;
-
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- jmp_hitlist_count = 0;
-
- target_block = get_target_block(cfg, block, 0, opt_count);
- while (target_block->len == 1) {
- target = op_array->opcodes + target_block->start;
-
- if (target->opcode == ZEND_JMP) {
- /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
- next = target_block->successors[0];
- } else if (target->opcode == last_op->opcode &&
- SAME_VAR(target->op1, last_op->op1)) {
- /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
- next = target_block->successors[0];
- } else if (target->opcode == INV_COND(last_op->opcode) &&
- SAME_VAR(target->op1, last_op->op1)) {
- /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
- next = target_block->successors[1];
- } else if (target->opcode == ZEND_JMPZNZ &&
- SAME_VAR(target->op1, last_op->op1)) {
- /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
- next = target_block->successors[last_op->opcode == ZEND_JMPNZ];
- } else {
- break;
- }
- CHECK_LOOP(next);
- block->successors[0] = next;
- ++(*opt_count);
- target_block = get_target_block(cfg, block, 0, opt_count);
- }
-
- follow_block = get_follow_block(cfg, block, 1, opt_count);
- if (target_block == follow_block) {
- /* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
- if (last_op->op1_type == IS_CV) {
- last_op->opcode = ZEND_CHECK_VAR;
- last_op->op2.num = 0;
- } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
- last_op->opcode = ZEND_FREE;
- last_op->op2.num = 0;
- } else {
- MAKE_NOP(last_op);
- block->len--;
- }
- block->successors_count = 1;
- ++(*opt_count);
- } else if (follow_block->len == 1) {
- target = op_array->opcodes + follow_block->start;
- if (target->opcode == ZEND_JMP) {
- if (block->successors[0] == follow_block->successors[0]) {
- /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
- if (last_op->op1_type == IS_CV) {
- last_op->opcode = ZEND_CHECK_VAR;
- last_op->op2.num = 0;
- } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
- last_op->opcode = ZEND_FREE;
- last_op->op2.num = 0;
- } else {
- MAKE_NOP(last_op);
- block->len--;
- }
- block->successors[0] = follow_block - cfg->blocks;
- block->successors_count = 1;
- ++(*opt_count);
- break;
- } else if (!(follow_block->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED))) {
- next_block = get_next_block(cfg, follow_block);
-
- if (target_block == next_block) {
- /* JMPZ(X,L1) JMP(L2) L1: -> JMPNZ(X,L2) NOP*/
-
- last_op->opcode = INV_COND(last_op->opcode);
-
- block->successors[0] = follow_block->successors[0];
- block->successors[1] = next_block - cfg->blocks;
-
- follow_block->flags &= ~ZEND_BB_REACHABLE;
- MAKE_NOP(target);
- follow_block->len = 0;
-
- next_block->flags |= ZEND_BB_FOLLOW;
-
- break;
- }
- }
-
- /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
- if (last_op->opcode == ZEND_JMPZ) {
- block->successors[1] = follow_block->successors[0];
- } else {
- block->successors[1] = block->successors[0];
- block->successors[0] = follow_block->successors[0];
- }
- last_op->opcode = ZEND_JMPZNZ;
- ++(*opt_count);
- }
- }
- break;
-
- case ZEND_JMPNZ_EX:
- case ZEND_JMPZ_EX:
- jmp_hitlist_count = 0;
-
- target_block = get_target_block(cfg, block, 0, opt_count);
- while (target_block->len == 1) {
- target = op_array->opcodes + target_block->start;
-
- if (target->opcode == ZEND_JMP) {
- /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
- next = target_block->successors[0];
- } else if (target->opcode == last_op->opcode-3 &&
- (SAME_VAR(target->op1, last_op->result) ||
- SAME_VAR(target->op1, last_op->op1))) {
- /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
- next = target_block->successors[0];
- } else if (target->opcode == last_op->opcode &&
- target->result.var == last_op->result.var &&
- (SAME_VAR(target->op1, last_op->result) ||
- SAME_VAR(target->op1, last_op->op1))) {
- /* T = JMPZ_EX(X, L1), L1: T = JMPZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L2) */
- next = target_block->successors[0];
- } else if (target->opcode == ZEND_JMPZNZ &&
- (SAME_VAR(target->op1, last_op->result) ||
- SAME_VAR(target->op1, last_op->op1))) {
- /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
- next = target_block->successors[last_op->opcode == ZEND_JMPNZ_EX];
- } else if (target->opcode == INV_EX_COND(last_op->opcode) &&
- (SAME_VAR(target->op1, last_op->result) ||
- SAME_VAR(target->op1, last_op->op1))) {
- /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
- next = target_block->successors[1];
- } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
- target->result.var == last_op->result.var &&
- (SAME_VAR(target->op1, last_op->result) ||
- SAME_VAR(target->op1, last_op->op1))) {
- /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L1+1) */
- next = target_block->successors[1];
- } else if (target->opcode == ZEND_BOOL &&
- (SAME_VAR(target->op1, last_op->result) ||
- SAME_VAR(target->op1, last_op->op1))) {
- /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
- Z = JMPZ_EX(X,L1+1) */
-
- /* NOTE: This optimization pattern is not safe, but works, */
- /* because result of JMPZ_EX instruction */
- /* is not used on the following path and */
- /* should be used once on the branch path. */
- /* */
- /* The pattern works well only if jums processed in */
- /* direct order, otherwise it breaks JMPZ_EX */
- /* sequences too early. */
- last_op->result.var = target->result.var;
- next = target_block->successors[0];
- } else {
- break;
- }
- CHECK_LOOP(next);
- block->successors[0] = next;
- ++(*opt_count);
- target_block = get_target_block(cfg, block, 0, opt_count);
- }
-
- follow_block = get_follow_block(cfg, block, 1, opt_count);
- if (target_block == follow_block) {
- /* L: T = JMP[N]Z_EX(X, L+1) -> T = BOOL(X) */
- last_op->opcode = ZEND_BOOL;
- last_op->op2.num = 0;
- block->successors_count = 1;
- ++(*opt_count);
- break;
- }
- break;
-
- case ZEND_JMPZNZ: {
-optimize_jmpznz:
- jmp_hitlist_count = 0;
- target_block = get_target_block(cfg, block, 0, opt_count);
- while (target_block->len == 1) {
- target = op_array->opcodes + target_block->start;
-
- if (target->opcode == ZEND_JMP) {
- /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
- next = target_block->successors[0];
- } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
- SAME_VAR(target->op1, last_op->op1)) {
- /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
- next = target_block->successors[0];
- } else if (target->opcode == ZEND_JMPNZ &&
- SAME_VAR(target->op1, last_op->op1)) {
- /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
- next = target_block->successors[1];
- } else {
- break;
- }
- CHECK_LOOP(next);
- block->successors[0] = next;
- ++(*opt_count);
- target_block = get_target_block(cfg, block, 0, opt_count);
- }
-
- jmp_hitlist_count = 0;
- follow_block = get_target_block(cfg, block, 1, opt_count);
- while (follow_block->len == 1) {
- target = op_array->opcodes + follow_block->start;
-
- if (target->opcode == ZEND_JMP) {
- /* JMPZNZ(X, L1, L2), L2: JMP(L3) -> JMPZNZ(X, L1, L3) */
- next = follow_block->successors[0];
- } else if (target->opcode == ZEND_JMPNZ &&
- SAME_VAR(target->op1, last_op->op1)) {
- /* JMPZNZ(X, L1, L2), L2: X = JMPNZ(X, L3) -> JMPZNZ(X, L1, L3) */
- next = follow_block->successors[0];
- } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
- SAME_VAR(target->op1, last_op->op1)) {
- /* JMPZNZ(X, L1, L2), L2: JMPZ(X, L3) -> JMPZNZ(X, L1, L2+1) */
- next = follow_block->successors[1];
- } else {
- break;
- }
- CHECK_LOOP(next);
- block->successors[1] = next;
- ++(*opt_count);
- follow_block = get_target_block(cfg, block, 1, opt_count);
- }
-
- next_block = get_next_block(cfg, block);
- if (target_block == follow_block &&
- !(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
- /* JMPZNZ(?,L,L) -> JMP(L) */
- last_op->opcode = ZEND_JMP;
- SET_UNUSED(last_op->op1);
- SET_UNUSED(last_op->op2);
- last_op->extended_value = 0;
- block->successors_count = 1;
- ++(*opt_count);
- } else if (target_block == next_block) {
- /* jumping to next on Z - can follow to it and jump only on NZ */
- /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
- int tmp = block->successors[0];
- last_op->opcode = ZEND_JMPNZ;
- block->successors[0] = block->successors[1];
- block->successors[1] = tmp;
- ++(*opt_count);
- } else if (follow_block == next_block) {
- /* jumping to next on NZ - can follow to it and jump only on Z */
- /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
- last_op->opcode = ZEND_JMPZ;
- ++(*opt_count);
- }
- break;
- }
- }
-}
-
-/* Global data dependencies */
-
-/* Find a set of variables which are used outside of the block where they are
- * defined. We won't apply some optimization patterns for such variables. */
-static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
-{
- int n;
- zend_basic_block *block, *next_block;
- uint32_t var_num;
- uint32_t bitset_len;
- zend_bitset usage;
- zend_bitset defined_here;
- void *checkpoint;
- zend_op *opline, *end;
-
-
- if (op_array->T == 0) {
- /* shortcut - if no Ts, nothing to do */
- return;
- }
-
- checkpoint = zend_arena_checkpoint(ctx->arena);
- bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
- defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
-
- zend_bitset_clear(defined_here, bitset_len);
- for (n = 1; n < cfg->blocks_count; n++) {
- block = cfg->blocks + n;
-
- if (!(block->flags & ZEND_BB_REACHABLE)) {
- continue;
- }
-
- opline = op_array->opcodes + block->start;
- end = opline + block->len;
- if (!(block->flags & ZEND_BB_FOLLOW) ||
- (block->flags & ZEND_BB_TARGET)) {
- /* Skip continuation of "extended" BB */
- zend_bitset_clear(defined_here, bitset_len);
- }
-
- while (opline<end) {
- if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
- var_num = VAR_NUM(opline->op1.var);
- if (!zend_bitset_in(defined_here, var_num)) {
- zend_bitset_incl(used_ext, var_num);
- }
- }
- if (opline->op2_type == IS_VAR) {
- var_num = VAR_NUM(opline->op2.var);
- if (opline->opcode == ZEND_FE_FETCH_R ||
- opline->opcode == ZEND_FE_FETCH_RW) {
- /* these opcode use the op2 as result */
- zend_bitset_incl(defined_here, var_num);
- } else if (!zend_bitset_in(defined_here, var_num)) {
- zend_bitset_incl(used_ext, var_num);
- }
- } else if (opline->op2_type == IS_TMP_VAR) {
- var_num = VAR_NUM(opline->op2.var);
- if (!zend_bitset_in(defined_here, var_num)) {
- zend_bitset_incl(used_ext, var_num);
- }
- }
-
- if (opline->result_type == IS_VAR) {
- var_num = VAR_NUM(opline->result.var);
- zend_bitset_incl(defined_here, var_num);
- } else if (opline->result_type == IS_TMP_VAR) {
- var_num = VAR_NUM(opline->result.var);
- switch (opline->opcode) {
- case ZEND_ADD_ARRAY_ELEMENT:
- case ZEND_ADD_ARRAY_UNPACK:
- case ZEND_ROPE_ADD:
- /* these opcodes use the result as argument */
- if (!zend_bitset_in(defined_here, var_num)) {
- zend_bitset_incl(used_ext, var_num);
- }
- break;
- default :
- zend_bitset_incl(defined_here, var_num);
- }
- }
- opline++;
- }
- }
-
- if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
- int printed = 0;
- uint32_t i;
-
- for (i = op_array->last_var; i< op_array->T; i++) {
- if (zend_bitset_in(used_ext, i)) {
- if (!printed) {
- fprintf(stderr, "NON-LOCAL-VARS: %d", i);
- printed = 1;
- } else {
- fprintf(stderr, ", %d", i);
- }
- }
- }
- if (printed) {
- fprintf(stderr, "\n");
- }
- }
-
- usage = defined_here;
- next_block = NULL;
- for (n = cfg->blocks_count; n > 0;) {
- block = cfg->blocks + (--n);
-
- if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) {
- continue;
- }
-
- end = op_array->opcodes + block->start;
- opline = end + block->len - 1;
- if (!next_block ||
- !(next_block->flags & ZEND_BB_FOLLOW) ||
- (next_block->flags & ZEND_BB_TARGET)) {
- /* Skip continuation of "extended" BB */
- zend_bitset_copy(usage, used_ext, bitset_len);
- } else if (block->successors_count > 1) {
- zend_bitset_union(usage, used_ext, bitset_len);
- }
- next_block = block;
-
- while (opline >= end) {
- /* usage checks */
- if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
- if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
- switch (opline->opcode) {
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_REF:
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- opline->result_type = IS_UNUSED;
- break;
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- case ZEND_POST_INC_STATIC_PROP:
- case ZEND_POST_DEC_STATIC_PROP:
- opline->opcode -= 2;
- opline->result_type = IS_UNUSED;
- break;
- case ZEND_QM_ASSIGN:
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- if (opline->op1_type == IS_CV) {
- opline->opcode = ZEND_CHECK_VAR;
- SET_UNUSED(opline->result);
- } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- opline->opcode = ZEND_FREE;
- SET_UNUSED(opline->result);
- } else {
- if (opline->op1_type == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- }
- MAKE_NOP(opline);
- }
- break;
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- opline->opcode -= 3;
- SET_UNUSED(opline->result);
- break;
- case ZEND_ADD_ARRAY_ELEMENT:
- case ZEND_ADD_ARRAY_UNPACK:
- case ZEND_ROPE_ADD:
- zend_bitset_incl(usage, VAR_NUM(opline->result.var));
- break;
- }
- } else {
- switch (opline->opcode) {
- case ZEND_ADD_ARRAY_ELEMENT:
- case ZEND_ADD_ARRAY_UNPACK:
- case ZEND_ROPE_ADD:
- break;
- default:
- zend_bitset_excl(usage, VAR_NUM(opline->result.var));
- break;
- }
- }
- }
-
- if (opline->op2_type == IS_VAR) {
- switch (opline->opcode) {
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- zend_bitset_excl(usage, VAR_NUM(opline->op2.var));
- break;
- default:
- zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
- break;
- }
- } else if (opline->op2_type == IS_TMP_VAR) {
- zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
- }
-
- if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
- zend_bitset_incl(usage, VAR_NUM(opline->op1.var));
- }
-
- opline--;
- }
- }
-
- zend_arena_release(&ctx->arena, checkpoint);
-}
-
-static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg, uint32_t *opt_count)
-{
- int i;
- zend_basic_block *b, *bb;
- zend_basic_block *prev = NULL;
-
- for (i = 0; i < cfg->blocks_count; i++) {
- b = cfg->blocks + i;
- if (b->flags & ZEND_BB_REACHABLE) {
- if ((b->flags & ZEND_BB_FOLLOW) &&
- !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
- prev && prev->successors_count == 1 && prev->successors[0] == i)
- {
- zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
- if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
- MAKE_NOP(last_op);
- }
-
- for (bb = prev + 1; bb != b; bb++) {
- zend_op *op = op_array->opcodes + bb->start;
- zend_op *end = op + bb->len;
- while (op < end) {
- if (op->op1_type == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(op));
- }
- if (op->op2_type == IS_CONST) {
- literal_dtor(&ZEND_OP2_LITERAL(op));
- }
- MAKE_NOP(op);
- op++;
- }
- /* make block empty */
- bb->len = 0;
- }
-
- /* re-link */
- prev->flags |= (b->flags & ZEND_BB_EXIT);
- prev->len = b->start + b->len - prev->start;
- prev->successors_count = b->successors_count;
- if (b->successors != b->successors_storage) {
- prev->successors = b->successors;
- b->successors = b->successors_storage;
- } else {
- memcpy(prev->successors, b->successors, b->successors_count * sizeof(int));
- }
-
- /* unlink & make block empty and unreachable */
- b->flags = 0;
- b->len = 0;
- b->successors_count = 0;
- ++(*opt_count);
- } else {
- prev = b;
- }
- }
- }
-}
-
-#define PASSES 3
-
-void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_cfg cfg;
- zend_basic_block *blocks, *end, *b;
- int pass;
- uint32_t bitset_len;
- zend_bitset usage;
- void *checkpoint;
- zend_op **Tsource;
- uint32_t opt_count;
- int *jmp_hitlist;
-
- /* Build CFG */
- checkpoint = zend_arena_checkpoint(ctx->arena);
- if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg) != SUCCESS) {
- zend_arena_release(&ctx->arena, checkpoint);
- return;
- }
-
- if (cfg.blocks_count * (op_array->last_var + op_array->T) > 64 * 1024 * 1024) {
- zend_arena_release(&ctx->arena, checkpoint);
- return;
- }
-
- if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
- zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
- }
-
- bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
- Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
- usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
- jmp_hitlist = zend_arena_alloc(&ctx->arena, cfg.blocks_count * sizeof(int));
-
- blocks = cfg.blocks;
- end = blocks + cfg.blocks_count;
- for (pass = 0; pass < PASSES; pass++) {
- opt_count = 0;
-
- /* Compute data dependencies */
- zend_bitset_clear(usage, bitset_len);
- zend_t_usage(&cfg, op_array, usage, ctx);
-
- /* optimize each basic block separately */
- for (b = blocks; b < end; b++) {
- if (!(b->flags & ZEND_BB_REACHABLE)) {
- continue;
- }
- /* we track data dependencies only inside a single basic block */
- if (!(b->flags & ZEND_BB_FOLLOW) ||
- (b->flags & ZEND_BB_TARGET)) {
- /* Skip continuation of "extended" BB */
- memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
- }
- zend_optimize_block(b, op_array, usage, &cfg, Tsource, &opt_count);
- }
-
- /* Eliminate NOPs */
- for (b = blocks; b < end; b++) {
- if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
- strip_nops(op_array, b);
- }
- }
-
- opt_count = 0;
-
- /* Jump optimization for each block */
- for (b = blocks; b < end; b++) {
- if (b->flags & ZEND_BB_REACHABLE) {
- zend_jmp_optimization(b, op_array, &cfg, jmp_hitlist, &opt_count);
- }
- }
-
- /* Eliminate unreachable basic blocks */
- zend_cfg_remark_reachable_blocks(op_array, &cfg);
-
- /* Merge Blocks */
- zend_merge_blocks(op_array, &cfg, &opt_count);
-
- if (opt_count == 0) {
- break;
- }
- }
-
- assemble_code_blocks(&cfg, op_array, ctx);
-
- if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
- zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
- }
-
- /* Destroy CFG */
- zend_arena_release(&ctx->arena, checkpoint);
-}
diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c
deleted file mode 100644
index 0e1529d2bd..0000000000
--- a/ext/opcache/Optimizer/compact_literals.c
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- | Xinchen Hui <laruence@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-/* pass 11
- * - compact literals table
- */
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-#include "zend_extensions.h"
-
-#define DEBUG_COMPACT_LITERALS 0
-
-#define LITERAL_VALUE 0x0100
-#define LITERAL_FUNC 0x0200
-#define LITERAL_CLASS 0x0300
-#define LITERAL_CONST 0x0400
-#define LITERAL_CLASS_CONST 0x0500
-#define LITERAL_STATIC_METHOD 0x0600
-#define LITERAL_STATIC_PROPERTY 0x0700
-#define LITERAL_METHOD 0x0800
-#define LITERAL_PROPERTY 0x0900
-#define LITERAL_GLOBAL 0x0A00
-
-#define LITERAL_KIND_MASK 0x0f00
-#define LITERAL_NUM_RELATED_MASK 0x000f
-
-#define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
-
-typedef struct _literal_info {
- uint32_t flags; /* bitmask (see defines above) */
-} literal_info;
-
-#define LITERAL_INFO(n, kind, related) do { \
- info[n].flags = ((kind) | (related)); \
- } while (0)
-
-static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
-{
- zend_arg_info *arg_info;
- if (arg_num > 0) {
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- return 0;
- }
- if (EXPECTED(arg_num <= op_array->num_args)) {
- arg_info = &op_array->arg_info[arg_num-1];
- } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
- arg_info = &op_array->arg_info[op_array->num_args];
- } else {
- return 0;
- }
- } else {
- arg_info = op_array->arg_info - 1;
- }
-
- if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
- if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
- return ZEND_TYPE_LIST(arg_info->type)->num_types;
- }
- return 1;
- }
-
- return 0;
-}
-
-static uint32_t add_static_slot(HashTable *hash,
- zend_op_array *op_array,
- uint32_t op1,
- uint32_t op2,
- uint32_t kind,
- int *cache_size)
-{
- uint32_t ret;
- zval *class_name = &op_array->literals[op1];
- zval *prop_name = &op_array->literals[op2];
- zval *pos, tmp;
-
- zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
- ZSTR_H(key) = zend_string_hash_func(key);
- ZSTR_H(key) += kind;
-
- pos = zend_hash_find(hash, key);
- if (pos) {
- ret = Z_LVAL_P(pos);
- } else {
- ret = *cache_size;
- *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
- ZVAL_LONG(&tmp, ret);
- zend_hash_add(hash, key, &tmp);
- }
- zend_string_release_ex(key, 0);
- return ret;
-}
-
-void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_op *opline, *end;
- int i, j, n, *map, cache_size;
- zval zv, *pos;
- literal_info *info;
- int l_null = -1;
- int l_false = -1;
- int l_true = -1;
- int l_empty_arr = -1;
- HashTable hash, double_hash;
- zend_string *key = NULL;
- void *checkpoint = zend_arena_checkpoint(ctx->arena);
- int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
-
- if (op_array->last_literal) {
- info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
-
- /* Mark literals of specific types */
- opline = op_array->opcodes;
- end = opline + op_array->last;
- while (opline < end) {
- switch (opline->opcode) {
- case ZEND_INIT_FCALL:
- LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
- break;
- case ZEND_INIT_FCALL_BY_NAME:
- LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
- break;
- case ZEND_INIT_NS_FCALL_BY_NAME:
- LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
- break;
- case ZEND_INIT_METHOD_CALL:
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
- }
- if (opline->op2_type == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
- }
- break;
- case ZEND_INIT_STATIC_METHOD_CALL:
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
- }
- if (opline->op2_type == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
- }
- break;
- case ZEND_CATCH:
- LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
- break;
- case ZEND_DEFINED:
- LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1);
- break;
- case ZEND_FETCH_CONSTANT:
- if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
- LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
- } else {
- LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 2);
- }
- break;
- case ZEND_FETCH_CLASS_CONSTANT:
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
- }
- LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1);
- break;
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- case ZEND_FETCH_STATIC_PROP_R:
- case ZEND_FETCH_STATIC_PROP_W:
- case ZEND_FETCH_STATIC_PROP_RW:
- case ZEND_FETCH_STATIC_PROP_IS:
- case ZEND_FETCH_STATIC_PROP_UNSET:
- case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
- case ZEND_UNSET_STATIC_PROP:
- case ZEND_ISSET_ISEMPTY_STATIC_PROP:
- case ZEND_PRE_INC_STATIC_PROP:
- case ZEND_PRE_DEC_STATIC_PROP:
- case ZEND_POST_INC_STATIC_PROP:
- case ZEND_POST_DEC_STATIC_PROP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- if (opline->op2_type == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
- }
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
- }
- break;
- case ZEND_FETCH_CLASS:
- case ZEND_INSTANCEOF:
- if (opline->op2_type == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
- }
- break;
- case ZEND_NEW:
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
- }
- break;
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_OBJ_W:
- case ZEND_FETCH_OBJ_RW:
- case ZEND_FETCH_OBJ_IS:
- case ZEND_FETCH_OBJ_UNSET:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_UNSET_OBJ:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- case ZEND_ASSIGN_OBJ_OP:
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
- }
- if (opline->op2_type == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
- }
- break;
- case ZEND_BIND_GLOBAL:
- LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
- break;
- case ZEND_RECV_INIT:
- LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
- break;
- case ZEND_DECLARE_FUNCTION:
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
- break;
- case ZEND_DECLARE_CLASS:
- case ZEND_DECLARE_CLASS_DELAYED:
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
- if (opline->op2_type == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
- }
- break;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- case ZEND_ASSIGN_DIM:
- case ZEND_UNSET_DIM:
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_R:
- case ZEND_FETCH_LIST_W:
- case ZEND_ASSIGN_DIM_OP:
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
- }
- if (opline->op2_type == IS_CONST) {
- if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
- LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
- } else {
- LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
- }
- }
- break;
- default:
- if (opline->op1_type == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
- }
- if (opline->op2_type == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
- }
- break;
- }
- opline++;
- }
-
-#if DEBUG_COMPACT_LITERALS
- {
- int i, use_copy;
- fprintf(stderr, "File %s func %s\n", op_array->filename->val,
- op_array->function_name ? op_array->function_name->val : "main");
- fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
-
- for (i = 0; i < op_array->last_literal; i++) {
- zval zv;
- ZVAL_COPY_VALUE(&zv, op_array->literals + i);
- use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
- fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
- if (use_copy) {
- zval_ptr_dtor_nogc(&zv);
- }
- }
- fflush(stderr);
- }
-#endif
-
- /* Merge equal constants */
- j = 0;
- zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
- /* Use separate hashtable for doubles stored as string keys, to avoid collisions. */
- zend_hash_init(&double_hash, 0, NULL, NULL, 0);
- map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
- memset(map, 0, op_array->last_literal * sizeof(int));
- for (i = 0; i < op_array->last_literal; i++) {
- if (!info[i].flags) {
- /* unset literal */
- zval_ptr_dtor_nogc(&op_array->literals[i]);
- continue;
- }
- switch (Z_TYPE(op_array->literals[i])) {
- case IS_NULL:
- if (l_null < 0) {
- l_null = j;
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- }
- map[i] = l_null;
- break;
- case IS_FALSE:
- if (l_false < 0) {
- l_false = j;
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- }
- map[i] = l_false;
- break;
- case IS_TRUE:
- if (l_true < 0) {
- l_true = j;
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- }
- map[i] = l_true;
- break;
- case IS_LONG:
- if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
- if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
- map[i] = Z_LVAL_P(pos);
- } else {
- map[i] = j;
- ZVAL_LONG(&zv, j);
- zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- }
- } else {
- ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2);
- key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
- ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 +
- LITERAL_NUM_RELATED(info[i].flags) - 1;
- if ((pos = zend_hash_find(&hash, key)) != NULL
- && LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) {
- map[i] = Z_LVAL_P(pos);
- zval_ptr_dtor_nogc(&op_array->literals[i+1]);
- } else {
- map[i] = j;
- ZVAL_LONG(&zv, j);
- zend_hash_add_new(&hash, key, &zv);
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- op_array->literals[j+1] = op_array->literals[i+1];
- info[j+1] = info[i+1];
- }
- j += 2;
- }
- zend_string_release_ex(key, 0);
- i++;
- }
- break;
- case IS_DOUBLE:
- if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
- map[i] = Z_LVAL_P(pos);
- } else {
- map[i] = j;
- ZVAL_LONG(&zv, j);
- zend_hash_str_add_new(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- }
- break;
- case IS_STRING: {
- if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
- key = zend_string_copy(Z_STR(op_array->literals[i]));
- } else if ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE) {
- key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
- ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i])) +
- LITERAL_NUM_RELATED(info[i].flags) - 1;
- } else {
- /* Don't merge LITERAL_VALUE that has related literals */
- key = NULL;
- }
- if (key && (pos = zend_hash_find(&hash, key)) != NULL &&
- Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
- LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
- (LITERAL_NUM_RELATED(info[i].flags) != 2 ||
- ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
- (info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
- zend_string_release_ex(key, 0);
- map[i] = Z_LVAL_P(pos);
- zval_ptr_dtor_nogc(&op_array->literals[i]);
- n = LITERAL_NUM_RELATED(info[i].flags);
- while (n > 1) {
- i++;
- zval_ptr_dtor_nogc(&op_array->literals[i]);
- n--;
- }
- } else {
- map[i] = j;
- ZVAL_LONG(&zv, j);
- if (key) {
- zend_hash_add_new(&hash, key, &zv);
- zend_string_release_ex(key, 0);
- }
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- n = LITERAL_NUM_RELATED(info[i].flags);
- while (n > 1) {
- i++;
- if (i != j) op_array->literals[j] = op_array->literals[i];
- j++;
- n--;
- }
- }
- break;
- }
- case IS_ARRAY:
- if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
- if (l_empty_arr < 0) {
- l_empty_arr = j;
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- } else {
- zval_ptr_dtor_nogc(&op_array->literals[i]);
- }
- map[i] = l_empty_arr;
- break;
- }
- /* break missing intentionally */
- default:
- /* don't merge other types */
- map[i] = j;
- if (i != j) {
- op_array->literals[j] = op_array->literals[i];
- info[j] = info[i];
- }
- j++;
- break;
- }
- }
-
- /* Only clean "hash", as it will be reused in the loop below. */
- zend_hash_clean(&hash);
- zend_hash_destroy(&double_hash);
- op_array->last_literal = j;
-
- const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
- memset(const_slot, -1, j * 6 * sizeof(int));
- class_slot = const_slot + j;
- func_slot = class_slot + j;
- bind_var_slot = func_slot + j;
- property_slot = bind_var_slot + j;
- method_slot = property_slot + j;
-
- /* Update opcodes to use new literals table */
- cache_size = zend_op_array_extension_handles * sizeof(void*);
- opline = op_array->opcodes;
- end = opline + op_array->last;
- while (opline < end) {
- if (opline->op1_type == IS_CONST) {
- opline->op1.constant = map[opline->op1.constant];
- }
- if (opline->op2_type == IS_CONST) {
- opline->op2.constant = map[opline->op2.constant];
- }
- switch (opline->opcode) {
- case ZEND_RECV_INIT:
- case ZEND_RECV:
- case ZEND_RECV_VARIADIC:
- {
- size_t num_classes = type_num_classes(op_array, opline->op1.num);
- if (num_classes) {
- opline->extended_value = cache_size;
- cache_size += num_classes * sizeof(void *);
- }
- break;
- }
- case ZEND_VERIFY_RETURN_TYPE:
- {
- size_t num_classes = type_num_classes(op_array, 0);
- if (num_classes) {
- opline->op2.num = cache_size;
- cache_size += num_classes * sizeof(void *);
- }
- break;
- }
- case ZEND_ASSIGN_STATIC_PROP_OP:
- if (opline->op1_type == IS_CONST) {
- // op1 static property
- if (opline->op2_type == IS_CONST) {
- (opline+1)->extended_value = add_static_slot(&hash, op_array,
- opline->op2.constant,
- opline->op1.constant,
- LITERAL_STATIC_PROPERTY,
- &cache_size);
- } else {
- (opline+1)->extended_value = cache_size;
- cache_size += 3 * sizeof(void *);
- }
- } else if (opline->op2_type == IS_CONST) {
- // op2 class
- if (class_slot[opline->op2.constant] >= 0) {
- (opline+1)->extended_value = class_slot[opline->op2.constant];
- } else {
- (opline+1)->extended_value = cache_size;
- class_slot[opline->op2.constant] = cache_size;
- cache_size += sizeof(void *);
- }
- }
- break;
- case ZEND_ASSIGN_OBJ_OP:
- if (opline->op2_type == IS_CONST) {
- // op2 property
- if (opline->op1_type == IS_UNUSED &&
- property_slot[opline->op2.constant] >= 0) {
- (opline+1)->extended_value = property_slot[opline->op2.constant];
- } else {
- (opline+1)->extended_value = cache_size;
- cache_size += 3 * sizeof(void *);
- if (opline->op1_type == IS_UNUSED) {
- property_slot[opline->op2.constant] = (opline+1)->extended_value;
- }
- }
- }
- break;
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_OBJ_W:
- case ZEND_FETCH_OBJ_RW:
- case ZEND_FETCH_OBJ_IS:
- case ZEND_FETCH_OBJ_UNSET:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_UNSET_OBJ:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- if (opline->op2_type == IS_CONST) {
- // op2 property
- if (opline->op1_type == IS_UNUSED &&
- property_slot[opline->op2.constant] >= 0) {
- opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
- } else {
- opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
- cache_size += 3 * sizeof(void *);
- if (opline->op1_type == IS_UNUSED) {
- property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
- }
- }
- }
- break;
- case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- if (opline->op2_type == IS_CONST) {
- // op2 property
- if (opline->op1_type == IS_UNUSED &&
- property_slot[opline->op2.constant] >= 0) {
- opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
- } else {
- opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
- cache_size += 3 * sizeof(void *);
- if (opline->op1_type == IS_UNUSED) {
- property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
- }
- }
- }
- break;
- case ZEND_INIT_FCALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- // op2 func
- if (func_slot[opline->op2.constant] >= 0) {
- opline->result.num = func_slot[opline->op2.constant];
- } else {
- opline->result.num = cache_size;
- cache_size += sizeof(void *);
- func_slot[opline->op2.constant] = opline->result.num;
- }
- break;
- case ZEND_INIT_METHOD_CALL:
- if (opline->op2_type == IS_CONST) {
- // op2 method
- if (opline->op1_type == IS_UNUSED &&
- method_slot[opline->op2.constant] >= 0) {
- opline->result.num = method_slot[opline->op2.constant];
- } else {
- opline->result.num = cache_size;
- cache_size += 2 * sizeof(void *);
- if (opline->op1_type == IS_UNUSED) {
- method_slot[opline->op2.constant] = opline->result.num;
- }
- }
- }
- break;
- case ZEND_INIT_STATIC_METHOD_CALL:
- if (opline->op2_type == IS_CONST) {
- // op2 static method
- if (opline->op1_type == IS_CONST) {
- opline->result.num = add_static_slot(&hash, op_array,
- opline->op1.constant,
- opline->op2.constant,
- LITERAL_STATIC_METHOD,
- &cache_size);
- } else {
- opline->result.num = cache_size;
- cache_size += 2 * sizeof(void *);
- }
- } else if (opline->op1_type == IS_CONST) {
- // op1 class
- if (class_slot[opline->op1.constant] >= 0) {
- opline->result.num = class_slot[opline->op1.constant];
- } else {
- opline->result.num = cache_size;
- cache_size += sizeof(void *);
- class_slot[opline->op1.constant] = opline->result.num;
- }
- }
- break;
- case ZEND_DEFINED:
- // op1 const
- if (const_slot[opline->op1.constant] >= 0) {
- opline->extended_value = const_slot[opline->op1.constant];
- } else {
- opline->extended_value = cache_size;
- cache_size += sizeof(void *);
- const_slot[opline->op1.constant] = opline->extended_value;
- }
- break;
- case ZEND_FETCH_CONSTANT:
- // op2 const
- if (const_slot[opline->op2.constant] >= 0) {
- opline->extended_value = const_slot[opline->op2.constant];
- } else {
- opline->extended_value = cache_size;
- cache_size += sizeof(void *);
- const_slot[opline->op2.constant] = opline->extended_value;
- }
- break;
- case ZEND_FETCH_CLASS_CONSTANT:
- if (opline->op1_type == IS_CONST) {
- // op1/op2 class_const
- opline->extended_value = add_static_slot(&hash, op_array,
- opline->op1.constant,
- opline->op2.constant,
- LITERAL_CLASS_CONST,
- &cache_size);
- } else {
- opline->extended_value = cache_size;
- cache_size += 2 * sizeof(void *);
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- case ZEND_FETCH_STATIC_PROP_R:
- case ZEND_FETCH_STATIC_PROP_W:
- case ZEND_FETCH_STATIC_PROP_RW:
- case ZEND_FETCH_STATIC_PROP_IS:
- case ZEND_FETCH_STATIC_PROP_UNSET:
- case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
- case ZEND_UNSET_STATIC_PROP:
- case ZEND_ISSET_ISEMPTY_STATIC_PROP:
- case ZEND_PRE_INC_STATIC_PROP:
- case ZEND_PRE_DEC_STATIC_PROP:
- case ZEND_POST_INC_STATIC_PROP:
- case ZEND_POST_DEC_STATIC_PROP:
- if (opline->op1_type == IS_CONST) {
- // op1 static property
- if (opline->op2_type == IS_CONST) {
- opline->extended_value = add_static_slot(&hash, op_array,
- opline->op2.constant,
- opline->op1.constant,
- LITERAL_STATIC_PROPERTY,
- &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
- } else {
- opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
- cache_size += 3 * sizeof(void *);
- }
- } else if (opline->op2_type == IS_CONST) {
- // op2 class
- if (class_slot[opline->op2.constant] >= 0) {
- opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
- } else {
- opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
- class_slot[opline->op2.constant] = cache_size;
- cache_size += sizeof(void *);
- }
- }
- break;
- case ZEND_FETCH_CLASS:
- case ZEND_INSTANCEOF:
- if (opline->op2_type == IS_CONST) {
- // op2 class
- if (class_slot[opline->op2.constant] >= 0) {
- opline->extended_value = class_slot[opline->op2.constant];
- } else {
- opline->extended_value = cache_size;
- cache_size += sizeof(void *);
- class_slot[opline->op2.constant] = opline->extended_value;
- }
- }
- break;
- case ZEND_NEW:
- if (opline->op1_type == IS_CONST) {
- // op1 class
- if (class_slot[opline->op1.constant] >= 0) {
- opline->op2.num = class_slot[opline->op1.constant];
- } else {
- opline->op2.num = cache_size;
- cache_size += sizeof(void *);
- class_slot[opline->op1.constant] = opline->op2.num;
- }
- }
- break;
- case ZEND_CATCH:
- if (opline->op1_type == IS_CONST) {
- // op1 class
- if (class_slot[opline->op1.constant] >= 0) {
- opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
- } else {
- opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
- cache_size += sizeof(void *);
- class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
- }
- }
- break;
- case ZEND_BIND_GLOBAL:
- // op2 bind var
- if (bind_var_slot[opline->op2.constant] >= 0) {
- opline->extended_value = bind_var_slot[opline->op2.constant];
- } else {
- opline->extended_value = cache_size;
- cache_size += sizeof(void *);
- bind_var_slot[opline->op2.constant] = opline->extended_value;
- }
- break;
- case ZEND_DECLARE_LAMBDA_FUNCTION:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_CLASS_DELAYED:
- opline->extended_value = cache_size;
- cache_size += sizeof(void *);
- break;
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- case ZEND_SEND_REF:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_CHECK_FUNC_ARG:
- if (opline->op2_type == IS_CONST) {
- opline->result.num = cache_size;
- cache_size += 2 * sizeof(void *);
- }
- break;
- }
- opline++;
- }
- op_array->cache_size = cache_size;
- zend_hash_destroy(&hash);
- zend_arena_release(&ctx->arena, checkpoint);
-
- if (1) {
- opline = op_array->opcodes;
- while (1) {
- if (opline->opcode == ZEND_RECV_INIT) {
- zval *val = &op_array->literals[opline->op2.constant];
-
- if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
- /* Ensure zval is aligned to 8 bytes */
- op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
- Z_CACHE_SLOT_P(val) = op_array->cache_size;
- op_array->cache_size += sizeof(zval);
- }
- } else if (opline->opcode != ZEND_RECV) {
- break;
- }
- opline++;
- }
- }
-
-#if DEBUG_COMPACT_LITERALS
- {
- int i, use_copy;
- fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
-
- for (i = 0; i < op_array->last_literal; i++) {
- zval zv;
- ZVAL_COPY_VALUE(&zv, op_array->literals + i);
- use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
- fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
- if (use_copy) {
- zval_ptr_dtor_nogc(&zv);
- }
- }
- fflush(stderr);
- }
-#endif
- }
-}
diff --git a/ext/opcache/Optimizer/compact_vars.c b/ext/opcache/Optimizer/compact_vars.c
deleted file mode 100644
index bf7a005787..0000000000
--- a/ext/opcache/Optimizer/compact_vars.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Removing unused variables |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Nikita Popov <nikic@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "ZendAccelerator.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_bitset.h"
-
-/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs.
- * This pass does not operate on SSA form anymore. */
-void zend_optimizer_compact_vars(zend_op_array *op_array) {
- int i;
-
- ALLOCA_FLAG(use_heap1);
- ALLOCA_FLAG(use_heap2);
- uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T);
- zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1);
- uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2);
- uint32_t num_cvs, num_tmps;
-
- /* Determine which CVs are used */
- zend_bitset_clear(used_vars, used_vars_len);
- for (i = 0; i < op_array->last; i++) {
- zend_op *opline = &op_array->opcodes[i];
- if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var));
- }
- if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var));
- }
- if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- zend_bitset_incl(used_vars, VAR_NUM(opline->result.var));
- if (opline->opcode == ZEND_ROPE_INIT) {
- uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
- while (num > 1) {
- num--;
- zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num);
- }
- }
- }
- }
-
- num_cvs = 0;
- for (i = 0; i < op_array->last_var; i++) {
- if (zend_bitset_in(used_vars, i)) {
- vars_map[i] = num_cvs++;
- } else {
- vars_map[i] = (uint32_t) -1;
- }
- }
-
- num_tmps = 0;
- for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) {
- if (zend_bitset_in(used_vars, i)) {
- vars_map[i] = num_cvs + num_tmps++;
- } else {
- vars_map[i] = (uint32_t) -1;
- }
- }
-
- free_alloca(used_vars, use_heap1);
- if (num_cvs == op_array->last_var && num_tmps == op_array->T) {
- free_alloca(vars_map, use_heap2);
- return;
- }
-
- ZEND_ASSERT(num_cvs <= op_array->last_var);
- ZEND_ASSERT(num_tmps <= op_array->T);
-
- /* Update CV and TMP references in opcodes */
- for (i = 0; i < op_array->last; i++) {
- zend_op *opline = &op_array->opcodes[i];
- if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]);
- }
- if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]);
- }
- if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]);
- }
- }
-
- /* Update CV name table */
- if (num_cvs != op_array->last_var) {
- if (num_cvs) {
- zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0);
- for (i = 0; i < op_array->last_var; i++) {
- if (vars_map[i] != (uint32_t) -1) {
- names[vars_map[i]] = op_array->vars[i];
- } else {
- zend_string_release_ex(op_array->vars[i], 0);
- }
- }
- efree(op_array->vars);
- op_array->vars = names;
- } else {
- for (i = 0; i < op_array->last_var; i++) {
- zend_string_release_ex(op_array->vars[i], 0);
- }
- efree(op_array->vars);
- op_array->vars = NULL;
- }
- op_array->last_var = num_cvs;
- }
-
- op_array->T = num_tmps;
-
- free_alloca(vars_map, use_heap2);
-}
diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c
deleted file mode 100644
index 91c9665d1d..0000000000
--- a/ext/opcache/Optimizer/dce.c
+++ /dev/null
@@ -1,610 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, DCE - Dead Code Elimination |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Nikita Popov <nikic@php.net> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "ZendAccelerator.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "Optimizer/zend_inference.h"
-#include "Optimizer/zend_ssa.h"
-#include "Optimizer/zend_func_info.h"
-#include "Optimizer/zend_call_graph.h"
-#include "zend_bitset.h"
-
-/* This pass implements a form of dead code elimination (DCE). The algorithm optimistically assumes
- * that all instructions and phis are dead. Instructions with immediate side-effects are then marked
- * as live. We then recursively (using a worklist) propagate liveness to the instructions that def
- * the used operands.
- *
- * Notes:
- * * This pass does not perform unreachable code elimination. This happens as part of the SCCP
- * pass.
- * * The DCE is performed without taking control-dependence into account, i.e. all conditional
- * branches are assumed to be live. It's possible to take control-dependence into account using
- * the DCE algorithm described by Cytron et al., however it requires the construction of a
- * postdominator tree and of postdominance frontiers, which does not seem worthwhile at this
- * point.
- * * We separate intrinsic side-effects from potential side-effects in the form of notices thrown
- * by the instruction (in case we want to make this configurable). See may_have_side_effects() and
- * zend_may_throw().
- * * We often cannot DCE assignments and unsets while guaranteeing that dtors run in the same
- * order. There is an optimization option to allow reordering of dtor effects.
- * * The algorithm is able to eliminate dead modifications of non-escaping arrays
- * and objects as well as dead arrays and objects allocations.
- */
-
-typedef struct {
- zend_ssa *ssa;
- zend_op_array *op_array;
- zend_bitset instr_dead;
- zend_bitset phi_dead;
- zend_bitset instr_worklist;
- zend_bitset phi_worklist;
- zend_bitset phi_worklist_no_val;
- uint32_t instr_worklist_len;
- uint32_t phi_worklist_len;
- unsigned reorder_dtor_effects : 1;
-} context;
-
-static inline zend_bool is_bad_mod(const zend_ssa *ssa, int use, int def) {
- if (def < 0) {
- /* This modification is not tracked by SSA, assume the worst */
- return 1;
- }
- if (ssa->var_info[use].type & MAY_BE_REF) {
- /* Modification of reference may have side-effect */
- return 1;
- }
- return 0;
-}
-
-static inline zend_bool may_have_side_effects(
- zend_op_array *op_array, zend_ssa *ssa,
- const zend_op *opline, const zend_ssa_op *ssa_op,
- zend_bool reorder_dtor_effects) {
- switch (opline->opcode) {
- case ZEND_NOP:
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_QM_ASSIGN:
- case ZEND_FREE:
- case ZEND_TYPE_CHECK:
- case ZEND_DEFINED:
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- case ZEND_POW:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- case ZEND_DIV:
- case ZEND_MOD:
- case ZEND_BOOL_XOR:
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- case ZEND_BW_NOT:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- case ZEND_CAST:
- case ZEND_ROPE_INIT:
- case ZEND_ROPE_ADD:
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- case ZEND_SPACESHIP:
- case ZEND_STRLEN:
- case ZEND_COUNT:
- case ZEND_GET_TYPE:
- case ZEND_ISSET_ISEMPTY_THIS:
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- case ZEND_FETCH_DIM_IS:
- case ZEND_ISSET_ISEMPTY_CV:
- case ZEND_ISSET_ISEMPTY_VAR:
- case ZEND_FETCH_IS:
- case ZEND_IN_ARRAY:
- case ZEND_FUNC_NUM_ARGS:
- case ZEND_FUNC_GET_ARGS:
- case ZEND_ARRAY_KEY_EXISTS:
- /* No side effects */
- return 0;
- case ZEND_ROPE_END:
- /* TODO: Rope dce optimization, see #76446 */
- return 1;
- case ZEND_JMP:
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- /* For our purposes a jumps and branches are side effects. */
- return 1;
- case ZEND_BEGIN_SILENCE:
- case ZEND_END_SILENCE:
- case ZEND_ECHO:
- case ZEND_INCLUDE_OR_EVAL:
- case ZEND_THROW:
- case ZEND_MATCH_ERROR:
- case ZEND_EXT_STMT:
- case ZEND_EXT_FCALL_BEGIN:
- case ZEND_EXT_FCALL_END:
- case ZEND_TICKS:
- case ZEND_YIELD:
- case ZEND_YIELD_FROM:
- /* Intrinsic side effects */
- return 1;
- case ZEND_DO_FCALL:
- case ZEND_DO_FCALL_BY_NAME:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- /* For now assume all calls have side effects */
- return 1;
- case ZEND_RECV:
- case ZEND_RECV_INIT:
- /* Even though RECV_INIT can be side-effect free, these cannot be simply dropped
- * due to the prologue skipping code. */
- return 1;
- case ZEND_ASSIGN_REF:
- return 1;
- case ZEND_ASSIGN:
- {
- if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)) {
- return 1;
- }
- if (!reorder_dtor_effects) {
- if (opline->op2_type != IS_CONST
- && (OP2_INFO() & MAY_HAVE_DTOR)
- && ssa->vars[ssa_op->op2_use].escape_state != ESCAPE_STATE_NO_ESCAPE) {
- /* DCE might shorten lifetime */
- return 1;
- }
- }
- return 0;
- }
- case ZEND_UNSET_VAR:
- return 1;
- case ZEND_UNSET_CV:
- {
- uint32_t t1 = OP1_INFO();
- if (t1 & MAY_BE_REF) {
- /* We don't consider uses as the LHS of an assignment as real uses during DCE, so
- * an unset may be considered dead even if there is a later assignment to the
- * variable. Removing the unset in this case would not be correct if the variable
- * is a reference, because unset breaks references. */
- return 1;
- }
- return 0;
- }
- case ZEND_PRE_INC:
- case ZEND_POST_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_DEC:
- return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def);
- case ZEND_ASSIGN_OP:
- return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
- || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE;
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
- || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
- return 1;
- }
- if (!reorder_dtor_effects) {
- opline++;
- ssa_op++;
- if (opline->op1_type != IS_CONST
- && (OP1_INFO() & MAY_HAVE_DTOR)) {
- /* DCE might shorten lifetime */
- return 1;
- }
- }
- return 0;
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
- || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
- return 1;
- }
- return 0;
- case ZEND_BIND_STATIC:
- if (op_array->static_variables
- && (opline->extended_value & ZEND_BIND_REF) != 0) {
- zval *value =
- (zval*)((char*)op_array->static_variables->arData +
- (opline->extended_value & ~ZEND_BIND_REF));
- if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
- /* AST may contain undefined constants */
- return 1;
- }
- }
- return 0;
- case ZEND_CHECK_VAR:
- return (OP1_INFO() & MAY_BE_UNDEF) != 0;
- default:
- /* For everything we didn't handle, assume a side-effect */
- return 1;
- }
-}
-
-static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) {
- zend_ssa_var *var = &ctx->ssa->vars[var_num];
- if (var->definition >= 0) {
- if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) {
- zend_bitset_incl(ctx->instr_worklist, var->definition);
- }
- } else if (var->definition_phi) {
- if (!check || zend_bitset_in(ctx->phi_dead, var_num)) {
- zend_bitset_incl(ctx->phi_worklist, var_num);
- }
- }
-}
-
-static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) {
- zend_ssa_var *var = &ctx->ssa->vars[var_num];
- if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) {
- zend_bitset_incl(ctx->phi_worklist_no_val, var_num);
- }
-}
-
-static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, zend_ssa *ssa, int check) {
- if (ssa_op->result_use >= 0) {
- add_to_worklists(ctx, ssa_op->result_use, check);
- }
- if (ssa_op->op1_use >= 0) {
- if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)
- || (opline->opcode == ZEND_ASSIGN
- && (ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF) != 0)) {
- add_to_worklists(ctx, ssa_op->op1_use, check);
- } else {
- add_to_phi_worklist_no_val(ctx, ssa_op->op1_use);
- }
- }
- if (ssa_op->op2_use >= 0) {
- if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)
- || (opline->opcode == ZEND_FE_FETCH_R
- && (ssa->var_info[ssa_op->op2_use].type & MAY_BE_REF) != 0)) {
- add_to_worklists(ctx, ssa_op->op2_use, check);
- } else {
- add_to_phi_worklist_no_val(ctx, ssa_op->op2_use);
- }
- }
-}
-
-static zend_always_inline void add_phi_sources_to_worklists(context *ctx, zend_ssa_phi *phi, int check) {
- zend_ssa *ssa = ctx->ssa;
- int source;
- FOREACH_PHI_SOURCE(phi, source) {
- add_to_worklists(ctx, source, check);
- } FOREACH_PHI_SOURCE_END();
-}
-
-static inline zend_bool is_var_dead(context *ctx, int var_num) {
- zend_ssa_var *var = &ctx->ssa->vars[var_num];
- if (var->definition_phi) {
- return zend_bitset_in(ctx->phi_dead, var_num);
- } else if (var->definition >= 0) {
- return zend_bitset_in(ctx->instr_dead, var->definition);
- } else {
- /* Variable has no definition, so either the definition has already been removed (var is
- * dead) or this is one of the implicit variables at the start of the function (for our
- * purposes live) */
- return var_num >= ctx->op_array->last_var;
- }
-}
-
-// Sometimes we can mark the var as EXT_UNUSED
-static zend_bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) {
- if (use_chain >= 0) {
- return 0;
- }
- zend_ssa_var *var = &ctx->ssa->vars[free_var];
- int def = var->definition;
-
- if (def >= 0) {
- zend_ssa_op *def_op = &ctx->ssa->ops[def];
-
- if (def_op->result_def == free_var
- && var->phi_use_chain == NULL
- && var->use_chain == (opline - ctx->op_array->opcodes)) {
- zend_op *def_opline = &ctx->op_array->opcodes[def];
-
- switch (def_opline->opcode) {
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_REF:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_PRE_INC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_DEC_OBJ:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- case ZEND_DO_FCALL:
- case ZEND_INCLUDE_OR_EVAL:
- case ZEND_YIELD:
- case ZEND_YIELD_FROM:
- case ZEND_ASSERT_CHECK:
- def_opline->result_type = IS_UNUSED;
- def_opline->result.var = 0;
- def_op->result_def = -1;
- var->definition = -1;
- return 1;
- default:
- break;
- }
- }
- }
- return 0;
-}
-
-/* Returns whether the instruction has been DCEd */
-static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
- zend_ssa *ssa = ctx->ssa;
- int free_var = -1;
- zend_uchar free_var_type;
-
- if (opline->opcode == ZEND_NOP) {
- return 0;
- }
-
- /* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */
- if (opline->opcode == ZEND_FREE
- && (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
- && !is_var_dead(ctx, ssa_op->op1_use)) {
- return 0;
- }
-
- if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
- if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
- if (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)
- && opline->opcode != ZEND_CASE
- && opline->opcode != ZEND_CASE_STRICT) {
- free_var = ssa_op->op1_use;
- free_var_type = opline->op1_type;
- }
- }
- }
- if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
- if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) {
- if (ssa->var_info[ssa_op->op2_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
- if (free_var >= 0) {
- // TODO: We can't free two vars. Keep instruction alive.
- zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
- return 0;
- }
- free_var = ssa_op->op2_use;
- free_var_type = opline->op2_type;
- }
- }
- }
-
- zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op);
- zend_ssa_remove_instr(ctx->ssa, opline, ssa_op);
-
- if (free_var >= 0) {
- opline->opcode = ZEND_FREE;
- opline->op1.var = EX_NUM_TO_VAR(ssa->vars[free_var].var);
- opline->op1_type = free_var_type;
-
- ssa_op->op1_use = free_var;
- ssa_op->op1_use_chain = ssa->vars[free_var].use_chain;
- ssa->vars[free_var].use_chain = ssa_op - ssa->ops;
- return 0;
- }
- return 1;
-}
-
-static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
- int common_source = -1;
- int source;
- FOREACH_PHI_SOURCE(phi, source) {
- if (common_source == -1) {
- common_source = source;
- } else if (common_source != source && source != phi->ssa_var) {
- return -1;
- }
- } FOREACH_PHI_SOURCE_END();
- ZEND_ASSERT(common_source != -1);
- return common_source;
-}
-
-static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) {
- zend_ssa *ssa = ctx->ssa;
- if (phi->pi < 0) {
- /* Phi assignment with identical source operands */
- int common_source = get_common_phi_source(ssa, phi);
- if (common_source >= 0) {
- zend_ssa_rename_var_uses(ssa, phi->ssa_var, common_source, 1);
- zend_ssa_remove_phi(ssa, phi);
- }
- } else {
- /* Pi assignment that is only used in Phi/Pi assignments */
- // TODO What if we want to rerun type inference after DCE? Maybe separate this?
- /*ZEND_ASSERT(phi->sources[0] != -1);
- if (ssa->vars[phi->ssa_var].use_chain < 0) {
- zend_ssa_rename_var_uses_keep_types(ssa, phi->ssa_var, phi->sources[0], 1);
- zend_ssa_remove_phi(ssa, phi);
- }*/
- }
-}
-
-static inline zend_bool may_break_varargs(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op) {
- if (ssa_op->op1_def >= 0
- && ssa->vars[ssa_op->op1_def].var < op_array->num_args) {
- return 1;
- }
- if (ssa_op->op2_def >= 0
- && ssa->vars[ssa_op->op2_def].var < op_array->num_args) {
- return 1;
- }
- if (ssa_op->result_def >= 0
- && ssa->vars[ssa_op->result_def].var < op_array->num_args) {
- return 1;
- }
- return 0;
-}
-
-int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects) {
- int i;
- zend_ssa_phi *phi;
- int removed_ops = 0;
-
- /* DCE of CV operations that changes arguments may affect vararg functions. */
- zend_bool has_varargs = (ssa->cfg.flags & ZEND_FUNC_VARARG) != 0;
-
- context ctx;
- ctx.ssa = ssa;
- ctx.op_array = op_array;
- ctx.reorder_dtor_effects = reorder_dtor_effects;
-
- /* We have no dedicated phi vector, so we use the whole ssa var vector instead */
- ctx.instr_worklist_len = zend_bitset_len(op_array->last);
- ctx.instr_worklist = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
- memset(ctx.instr_worklist, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
- ctx.phi_worklist_len = zend_bitset_len(ssa->vars_count);
- ctx.phi_worklist = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
- memset(ctx.phi_worklist, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
- ctx.phi_worklist_no_val = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
- memset(ctx.phi_worklist_no_val, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
-
- /* Optimistically assume all instructions and phis to be dead */
- ctx.instr_dead = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
- memset(ctx.instr_dead, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
- ctx.phi_dead = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
- memset(ctx.phi_dead, 0xff, sizeof(zend_ulong) * ctx.phi_worklist_len);
-
- /* Mark reacable instruction without side effects as dead */
- int b = ssa->cfg.blocks_count;
- while (b > 0) {
- int op_data = -1;
-
- b--;
- zend_basic_block *block = &ssa->cfg.blocks[b];
- if (!(block->flags & ZEND_BB_REACHABLE)) {
- continue;
- }
- i = block->start + block->len;
- while (i > block->start) {
- i--;
-
- if (op_array->opcodes[i].opcode == ZEND_OP_DATA) {
- op_data = i;
- continue;
- }
-
- if (zend_bitset_in(ctx.instr_worklist, i)) {
- zend_bitset_excl(ctx.instr_worklist, i);
- add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0);
- if (op_data >= 0) {
- add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0);
- }
- } else if (may_have_side_effects(op_array, ssa, &op_array->opcodes[i], &ssa->ops[i], ctx.reorder_dtor_effects)
- || zend_may_throw(&op_array->opcodes[i], &ssa->ops[i], op_array, ssa)
- || (has_varargs && may_break_varargs(op_array, ssa, &ssa->ops[i]))) {
- if (op_array->opcodes[i].opcode == ZEND_NEW
- && op_array->opcodes[i+1].opcode == ZEND_DO_FCALL
- && ssa->ops[i].result_def >= 0
- && ssa->vars[ssa->ops[i].result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- zend_bitset_incl(ctx.instr_dead, i);
- zend_bitset_incl(ctx.instr_dead, i+1);
- } else {
- add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0);
- if (op_data >= 0) {
- add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0);
- }
- }
- } else {
- zend_bitset_incl(ctx.instr_dead, i);
- if (op_data >= 0) {
- zend_bitset_incl(ctx.instr_dead, op_data);
- }
- }
- op_data = -1;
- }
- }
-
- /* Propagate liveness backwards to all definitions of used vars */
- while (!zend_bitset_empty(ctx.instr_worklist, ctx.instr_worklist_len)
- || !zend_bitset_empty(ctx.phi_worklist, ctx.phi_worklist_len)) {
- while ((i = zend_bitset_pop_first(ctx.instr_worklist, ctx.instr_worklist_len)) >= 0) {
- zend_bitset_excl(ctx.instr_dead, i);
- add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 1);
- if (i < op_array->last && op_array->opcodes[i+1].opcode == ZEND_OP_DATA) {
- zend_bitset_excl(ctx.instr_dead, i+1);
- add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], ssa, 1);
- }
- }
- while ((i = zend_bitset_pop_first(ctx.phi_worklist, ctx.phi_worklist_len)) >= 0) {
- zend_bitset_excl(ctx.phi_dead, i);
- zend_bitset_excl(ctx.phi_worklist_no_val, i);
- add_phi_sources_to_worklists(&ctx, ssa->vars[i].definition_phi, 1);
- }
- }
-
- /* Eliminate dead instructions */
- ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) {
- removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]);
- } ZEND_BITSET_FOREACH_END();
-
- /* Improper uses don't count as "uses" for the purpose of instruction elimination,
- * but we have to retain phis defining them.
- * Propagate this information backwards, marking any phi with an improperly used
- * target as non-dead. */
- while ((i = zend_bitset_pop_first(ctx.phi_worklist_no_val, ctx.phi_worklist_len)) >= 0) {
- zend_ssa_phi *phi = ssa->vars[i].definition_phi;
- int source;
- zend_bitset_excl(ctx.phi_dead, i);
- FOREACH_PHI_SOURCE(phi, source) {
- add_to_phi_worklist_no_val(&ctx, source);
- } FOREACH_PHI_SOURCE_END();
- }
-
- /* Now collect the actually dead phis */
- FOREACH_PHI(phi) {
- if (zend_bitset_in(ctx.phi_dead, phi->ssa_var)) {
- zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
- zend_ssa_remove_phi(ssa, phi);
- } else {
- /* Remove trivial phis (phis with identical source operands) */
- try_remove_trivial_phi(&ctx, phi);
- }
- } FOREACH_PHI_END();
-
- return removed_ops;
-}
diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c
deleted file mode 100644
index 0ac6c563d0..0000000000
--- a/ext/opcache/Optimizer/dfa_pass.c
+++ /dev/null
@@ -1,1637 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-#include "zend_bitset.h"
-#include "zend_cfg.h"
-#include "zend_ssa.h"
-#include "zend_func_info.h"
-#include "zend_call_graph.h"
-#include "zend_inference.h"
-#include "zend_dump.h"
-
-#ifndef ZEND_DEBUG_DFA
-# define ZEND_DEBUG_DFA ZEND_DEBUG
-#endif
-
-#if ZEND_DEBUG_DFA
-# include "ssa_integrity.c"
-#endif
-
-int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
-{
- uint32_t build_flags;
-
- if (op_array->last_try_catch) {
- /* TODO: we can't analyze functions with try/catch/finally ??? */
- return FAILURE;
- }
-
- /* Build SSA */
- memset(ssa, 0, sizeof(zend_ssa));
-
- if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg) != SUCCESS) {
- return FAILURE;
- }
-
- if ((ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
- /* TODO: we can't analyze functions with indirect variable access ??? */
- return FAILURE;
- }
-
- if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) {
- return FAILURE;
- }
-
- if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
- zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg);
- }
-
- /* Compute Dominators Tree */
- if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) {
- return FAILURE;
- }
-
- /* Identify reducible and irreducible loops */
- if (zend_cfg_identify_loops(op_array, &ssa->cfg) != SUCCESS) {
- return FAILURE;
- }
-
- if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
- zend_dump_dominators(op_array, &ssa->cfg);
- }
-
- 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, ctx->script, op_array, build_flags, ssa) != SUCCESS) {
- return FAILURE;
- }
-
- if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
- zend_dump_op_array(op_array, ZEND_DUMP_SSA, "dfa ssa", ssa);
- }
-
-
- if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){
- return FAILURE;
- }
-
- if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
- return FAILURE;
- }
-
- if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
- return FAILURE;
- }
-
- if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa, ctx->optimization_level) != SUCCESS) {
- return FAILURE;
- }
-
- if (zend_ssa_escape_analysis(ctx->script, op_array, ssa) != SUCCESS) {
- return FAILURE;
- }
-
- if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
- zend_dump_ssa_variables(op_array, ssa, 0);
- }
-
- return SUCCESS;
-}
-
-static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_optimizer_ctx *ctx)
-{
- zend_basic_block *blocks = ssa->cfg.blocks;
- zend_basic_block *blocks_end = blocks + ssa->cfg.blocks_count;
- zend_basic_block *b;
- zend_func_info *func_info;
- int j;
- uint32_t i = 0;
- uint32_t target = 0;
- uint32_t *shiftlist;
- ALLOCA_FLAG(use_heap);
-
- shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
- memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
- /* remove empty callee_info */
- func_info = ZEND_FUNC_INFO(op_array);
- if (func_info) {
- zend_call_info **call_info = &func_info->callee_info;
- while ((*call_info)) {
- if ((*call_info)->caller_init_opline->opcode == ZEND_NOP) {
- *call_info = (*call_info)->next_callee;
- } else {
- call_info = &(*call_info)->next_callee;
- }
- }
- }
-
- for (b = blocks; b < blocks_end; b++) {
- if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
- if (b->len) {
- uint32_t new_start, old_end;
- while (i < b->start) {
- shiftlist[i] = i - target;
- i++;
- }
-
- if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
- /* Only keep the FREE for the loop var */
- ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
- || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
- b->len = 1;
- }
-
- new_start = target;
- old_end = b->start + b->len;
- while (i < old_end) {
- shiftlist[i] = i - target;
- if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP)) {
- if (i != target) {
- op_array->opcodes[target] = op_array->opcodes[i];
- ssa->ops[target] = ssa->ops[i];
- ssa->cfg.map[target] = b - blocks;
- }
- target++;
- }
- i++;
- }
- b->start = new_start;
- if (target != old_end) {
- zend_op *opline;
- zend_op *new_opline;
-
- b->len = target - b->start;
- opline = op_array->opcodes + old_end - 1;
- if (opline->opcode == ZEND_NOP) {
- continue;
- }
-
- new_opline = op_array->opcodes + target - 1;
- zend_optimizer_migrate_jump(op_array, new_opline, opline);
- }
- } else {
- b->start = target;
- }
- } else {
- b->start = target;
- b->len = 0;
- }
- }
-
- if (target != op_array->last) {
- /* reset rest opcodes */
- for (i = target; i < op_array->last; i++) {
- MAKE_NOP(op_array->opcodes + i);
- }
-
- /* update SSA variables */
- for (j = 0; j < ssa->vars_count; j++) {
- if (ssa->vars[j].definition >= 0) {
- ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
- }
- if (ssa->vars[j].use_chain >= 0) {
- ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
- }
- }
- for (i = 0; i < op_array->last; i++) {
- if (ssa->ops[i].op1_use_chain >= 0) {
- ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
- }
- if (ssa->ops[i].op2_use_chain >= 0) {
- ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
- }
- if (ssa->ops[i].res_use_chain >= 0) {
- ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
- }
- }
-
- /* update branch targets */
- for (b = blocks; b < blocks_end; b++) {
- if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
- zend_op *opline = op_array->opcodes + b->start + b->len - 1;
- zend_optimizer_shift_jump(op_array, opline, shiftlist);
- }
- }
-
- /* update try/catch array */
- for (j = 0; j < op_array->last_try_catch; j++) {
- op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
- op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
- if (op_array->try_catch_array[j].finally_op) {
- op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
- op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
- }
- }
-
- /* update early binding list */
- if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
- uint32_t *opline_num = &ctx->script->first_early_binding_opline;
-
- ZEND_ASSERT(op_array == &ctx->script->main_op_array);
- do {
- *opline_num -= shiftlist[*opline_num];
- opline_num = &op_array->opcodes[*opline_num].result.opline_num;
- } while (*opline_num != (uint32_t)-1);
- }
-
- /* update call graph */
- if (func_info) {
- zend_call_info *call_info = func_info->callee_info;
- while (call_info) {
- call_info->caller_init_opline -=
- shiftlist[call_info->caller_init_opline - op_array->opcodes];
- if (call_info->caller_call_opline) {
- call_info->caller_call_opline -=
- shiftlist[call_info->caller_call_opline - op_array->opcodes];
- }
- call_info = call_info->next_callee;
- }
- }
-
- op_array->last = target;
- }
- free_alloca(shiftlist, use_heap);
-}
-
-static zend_bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
- if (ce1 == ce2) {
- return 1;
- }
- if (!(ce1->ce_flags & ZEND_ACC_LINKED)) {
- /* This case could be generalized, similarly to unlinked_instanceof */
- return 0;
- }
- return instanceof_function(ce1, ce2);
-}
-
-static inline zend_bool can_elide_return_type_check(
- zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) {
- zend_arg_info *info = &op_array->arg_info[-1];
- zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
- zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def];
-
- /* TODO: It would be better to rewrite this without using def_info,
- * which may not be an exact representation of the type. */
- if (use_info->type & MAY_BE_REF) {
- return 0;
- }
-
- /* A type is possible that is not in the allowed types */
- if ((use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~(def_info->type & MAY_BE_ANY)) {
- return 0;
- }
-
- /* These types are not represented exactly */
- if (ZEND_TYPE_FULL_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE|MAY_BE_STATIC)) {
- return 0;
- }
-
- if (ZEND_TYPE_HAS_CLASS(info->type)) {
- if (!use_info->ce || !def_info->ce || !safe_instanceof(use_info->ce, def_info->ce)) {
- return 0;
- }
- }
-
- return 1;
-}
-
-static zend_bool opline_supports_assign_contraction(
- zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
- if (opline->opcode == ZEND_NEW) {
- /* see Zend/tests/generators/aborted_yield_during_new.phpt */
- return 0;
- }
-
- if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
- || opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
- /* Function calls may dtor the return value after it has already been written -- allow
- * direct assignment only for types where a double-dtor does not matter. */
- uint32_t type = ssa->var_info[src_var].type;
- uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE;
- return !((type & MAY_BE_ANY) & ~simple);
- }
-
- if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
- /* POST_INC/DEC write the result variable before performing the inc/dec. For $i = $i++
- * eliding the temporary variable would thus yield an incorrect result. */
- return opline->op1_type != IS_CV || opline->op1.var != cv_var;
- }
-
- if (opline->opcode == ZEND_INIT_ARRAY) {
- /* INIT_ARRAY initializes the result array before reading key/value. */
- return (opline->op1_type != IS_CV || opline->op1.var != cv_var)
- && (opline->op2_type != IS_CV || opline->op2.var != cv_var);
- }
-
- if (opline->opcode == ZEND_CAST
- && (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
- /* CAST to array/object may initialize the result to an empty array/object before
- * reading the expression. */
- return opline->op1_type != IS_CV || opline->op1.var != cv_var;
- }
-
- return 1;
-}
-
-int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
-{
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- int removed_ops = 0;
-
- if (func_info->callee_info) {
- zend_call_info *call_info = func_info->callee_info;
-
- do {
- if (call_info->caller_call_opline
- && call_info->caller_call_opline->opcode == ZEND_DO_ICALL
- && call_info->callee_func
- && zend_string_equals_literal(call_info->callee_func->common.function_name, "in_array")
- && (call_info->caller_init_opline->extended_value == 2
- || (call_info->caller_init_opline->extended_value == 3
- && (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
- && (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
-
- zend_op *send_array;
- zend_op *send_needly;
- zend_bool strict = 0;
-
- if (call_info->caller_init_opline->extended_value == 2) {
- send_array = call_info->caller_call_opline - 1;
- send_needly = call_info->caller_call_opline - 2;
- } else {
- if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
- strict = 1;
- }
- send_array = call_info->caller_call_opline - 2;
- send_needly = call_info->caller_call_opline - 3;
- }
-
- if (send_array->opcode == ZEND_SEND_VAL
- && send_array->op1_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
- && (send_needly->opcode == ZEND_SEND_VAL
- || send_needly->opcode == ZEND_SEND_VAR)
- ) {
- int ok = 1;
-
- HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
- HashTable *dst;
- zval *val, tmp;
- zend_ulong idx;
-
- ZVAL_TRUE(&tmp);
- dst = zend_new_array(zend_hash_num_elements(src));
- if (strict) {
- ZEND_HASH_FOREACH_VAL(src, val) {
- if (Z_TYPE_P(val) == IS_STRING) {
- zend_hash_add(dst, Z_STR_P(val), &tmp);
- } else if (Z_TYPE_P(val) == IS_LONG) {
- zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
- } else {
- zend_array_destroy(dst);
- ok = 0;
- break;
- }
- } ZEND_HASH_FOREACH_END();
- } else {
- ZEND_HASH_FOREACH_VAL(src, val) {
- if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
- zend_array_destroy(dst);
- ok = 0;
- break;
- }
- zend_hash_add(dst, Z_STR_P(val), &tmp);
- } ZEND_HASH_FOREACH_END();
- }
-
- if (ok) {
- uint32_t op_num = send_needly - op_array->opcodes;
- zend_ssa_op *ssa_op = ssa->ops + op_num;
-
- if (ssa_op->op1_use >= 0) {
- /* Reconstruct SSA */
- int var_num = ssa_op->op1_use;
- zend_ssa_var *var = ssa->vars + var_num;
-
- ZEND_ASSERT(ssa_op->op1_def < 0);
- zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
- ssa_op->op1_use = -1;
- ssa_op->op1_use_chain = -1;
- op_num = call_info->caller_call_opline - op_array->opcodes;
- ssa_op = ssa->ops + op_num;
- ssa_op->op1_use = var_num;
- ssa_op->op1_use_chain = var->use_chain;
- var->use_chain = op_num;
- }
-
- ZVAL_ARR(&tmp, dst);
-
- /* Update opcode */
- call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
- call_info->caller_call_opline->extended_value = strict;
- call_info->caller_call_opline->op1_type = send_needly->op1_type;
- call_info->caller_call_opline->op1.num = send_needly->op1.num;
- call_info->caller_call_opline->op2_type = IS_CONST;
- call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
- if (call_info->caller_init_opline->extended_value == 3) {
- MAKE_NOP(call_info->caller_call_opline - 1);
- }
- MAKE_NOP(call_info->caller_init_opline);
- MAKE_NOP(send_needly);
- MAKE_NOP(send_array);
- removed_ops++;
-
- op_num = call_info->caller_call_opline - op_array->opcodes;
- ssa_op = ssa->ops + op_num;
- if (ssa_op->result_def >= 0) {
- int var = ssa_op->result_def;
- int use = ssa->vars[var].use_chain;
-
- /* If the result is used only in a JMPZ/JMPNZ, replace result type with
- * IS_TMP_VAR, which will enable use of smart branches. Don't do this
- * in other cases, as not all opcodes support both VAR and TMP. */
- if (ssa->vars[var].phi_use_chain == NULL
- && ssa->ops[use].op1_use == var
- && ssa->ops[use].op1_use_chain == -1
- && (op_array->opcodes[use].opcode == ZEND_JMPZ
- || op_array->opcodes[use].opcode == ZEND_JMPNZ)) {
- call_info->caller_call_opline->result_type = IS_TMP_VAR;
- op_array->opcodes[use].op1_type = IS_TMP_VAR;
- }
- }
- }
- }
- }
- call_info = call_info->next_callee;
- } while (call_info);
- }
-
- return removed_ops;
-}
-
-static zend_always_inline void take_successor_0(zend_ssa *ssa, int block_num, zend_basic_block *block)
-{
- if (block->successors_count == 2) {
- if (block->successors[1] != block->successors[0]) {
- zend_ssa_remove_predecessor(ssa, block_num, block->successors[1]);
- }
- block->successors_count = 1;
- }
-}
-
-static zend_always_inline void take_successor_1(zend_ssa *ssa, int block_num, zend_basic_block *block)
-{
- if (block->successors_count == 2) {
- if (block->successors[1] != block->successors[0]) {
- zend_ssa_remove_predecessor(ssa, block_num, block->successors[0]);
- block->successors[0] = block->successors[1];
- }
- block->successors_count = 1;
- }
-}
-
-static zend_always_inline void take_successor_ex(zend_ssa *ssa, int block_num, zend_basic_block *block, int target_block)
-{
- int i;
-
- for (i = 0; i < block->successors_count; i++) {
- if (block->successors[i] != target_block) {
- zend_ssa_remove_predecessor(ssa, block_num, block->successors[i]);
- }
- }
- block->successors[0] = target_block;
- block->successors_count = 1;
-}
-
-static void compress_block(zend_op_array *op_array, zend_basic_block *block)
-{
- while (block->len > 0) {
- zend_op *opline = &op_array->opcodes[block->start + block->len - 1];
-
- if (opline->opcode == ZEND_NOP) {
- block->len--;
- } else {
- break;
- }
- }
-}
-
-static void replace_predecessor(zend_ssa *ssa, int block_id, int old_pred, int new_pred) {
- zend_basic_block *block = &ssa->cfg.blocks[block_id];
- int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
- zend_ssa_phi *phi;
-
- int i;
- int old_pred_idx = -1;
- int new_pred_idx = -1;
- for (i = 0; i < block->predecessors_count; i++) {
- if (predecessors[i] == old_pred) {
- old_pred_idx = i;
- }
- if (predecessors[i] == new_pred) {
- new_pred_idx = i;
- }
- }
-
- ZEND_ASSERT(old_pred_idx != -1);
- if (new_pred_idx == -1) {
- /* If the new predecessor doesn't exist yet, simply rewire the old one */
- predecessors[old_pred_idx] = new_pred;
- } else {
- /* Otherwise, rewiring the old predecessor would make the new predecessor appear
- * twice, which violates our CFG invariants. Remove the old predecessor instead. */
- memmove(
- predecessors + old_pred_idx,
- predecessors + old_pred_idx + 1,
- sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
- );
-
- /* Also remove the corresponding phi node entries */
- for (phi = ssa->blocks[block_id].phis; phi; phi = phi->next) {
- memmove(
- phi->sources + old_pred_idx,
- phi->sources + old_pred_idx + 1,
- sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
- );
- }
-
- block->predecessors_count--;
- }
-}
-
-static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa, int from, int to, int new_to)
-{
- zend_basic_block *src = &ssa->cfg.blocks[from];
- zend_basic_block *old = &ssa->cfg.blocks[to];
- zend_basic_block *dst = &ssa->cfg.blocks[new_to];
- int i;
- zend_op *opline;
-
- for (i = 0; i < src->successors_count; i++) {
- if (src->successors[i] == to) {
- src->successors[i] = new_to;
- }
- }
-
- if (src->len > 0) {
- opline = op_array->opcodes + src->start + src->len - 1;
- switch (opline->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- ZEND_ASSERT(ZEND_OP1_JMP_ADDR(opline) == op_array->opcodes + old->start);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + dst->start);
- break;
- case ZEND_JMPZNZ:
- if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
- }
- /* break missing intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) {
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start);
- }
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) {
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start);
- }
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
- }
- break;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- {
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
- zval *zv;
- ZEND_HASH_FOREACH_VAL(jumptable, zv) {
- if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) == old->start) {
- Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
- }
- } ZEND_HASH_FOREACH_END();
- if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
- }
- break;
- }
- }
- }
-
- replace_predecessor(ssa, new_to, to, from);
-}
-
-static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *block, int block_num)
-{
- if (block->predecessors_count == 1 && ssa->blocks[block_num].phis == NULL) {
- int *predecessors, i;
-
- ZEND_ASSERT(block->successors_count == 1);
- predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
- for (i = 0; i < block->predecessors_count; i++) {
- zend_ssa_replace_control_link(op_array, ssa, predecessors[i], block_num, block->successors[0]);
- }
- zend_ssa_remove_block(op_array, ssa, block_num);
- }
-}
-
-static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
-{
- int removed_ops = 0;
- int block_num = 0;
-
- for (block_num = 1; block_num < ssa->cfg.blocks_count; block_num++) {
- zend_basic_block *block = &ssa->cfg.blocks[block_num];
-
- if (!(block->flags & ZEND_BB_REACHABLE)) {
- continue;
- }
- compress_block(op_array, block);
- if (block->len == 0) {
- zend_ssa_unlink_block(op_array, ssa, block, block_num);
- }
- }
-
- block_num = 0;
- while (block_num < ssa->cfg.blocks_count
- && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE)) {
- block_num++;
- }
- while (block_num < ssa->cfg.blocks_count) {
- int next_block_num = block_num + 1;
- zend_basic_block *block = &ssa->cfg.blocks[block_num];
- uint32_t op_num;
- zend_op *opline;
- zend_ssa_op *ssa_op;
-
- while (next_block_num < ssa->cfg.blocks_count
- && !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) {
- next_block_num++;
- }
-
- if (block->len) {
- op_num = block->start + block->len - 1;
- opline = op_array->opcodes + op_num;
- ssa_op = ssa->ops + op_num;
-
- switch (opline->opcode) {
- case ZEND_JMP:
-optimize_jmp:
- if (block->successors[0] == next_block_num) {
- MAKE_NOP(opline);
- removed_ops++;
- goto optimize_nop;
- }
- break;
- case ZEND_JMPZ:
-optimize_jmpz:
- if (opline->op1_type == IS_CONST) {
- if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
- MAKE_NOP(opline);
- removed_ops++;
- take_successor_1(ssa, block_num, block);
- goto optimize_nop;
- } else {
- opline->opcode = ZEND_JMP;
- COPY_NODE(opline->op1, opline->op2);
- take_successor_0(ssa, block_num, block);
- goto optimize_jmp;
- }
- } else {
- if (block->successors[0] == next_block_num) {
- take_successor_0(ssa, block_num, block);
- if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
- opline->opcode = ZEND_CHECK_VAR;
- opline->op2.num = 0;
- } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
- zend_ssa_remove_instr(ssa, opline, ssa_op);
- removed_ops++;
- goto optimize_nop;
- } else {
- opline->opcode = ZEND_FREE;
- opline->op2.num = 0;
- }
- }
- }
- break;
- case ZEND_JMPNZ:
-optimize_jmpnz:
- if (opline->op1_type == IS_CONST) {
- if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
- opline->opcode = ZEND_JMP;
- COPY_NODE(opline->op1, opline->op2);
- take_successor_0(ssa, block_num, block);
- goto optimize_jmp;
- } else {
- MAKE_NOP(opline);
- removed_ops++;
- take_successor_1(ssa, block_num, block);
- goto optimize_nop;
- }
- } else if (block->successors_count == 2) {
- if (block->successors[0] == next_block_num) {
- take_successor_0(ssa, block_num, block);
- if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
- opline->opcode = ZEND_CHECK_VAR;
- opline->op2.num = 0;
- } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
- zend_ssa_remove_instr(ssa, opline, ssa_op);
- removed_ops++;
- goto optimize_nop;
- } else {
- opline->opcode = ZEND_FREE;
- opline->op2.num = 0;
- }
- }
- }
- break;
- case ZEND_JMPZNZ:
- if (opline->op1_type == IS_CONST) {
- if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
- zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
- take_successor_1(ssa, block_num, block);
- } else {
- zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
- take_successor_0(ssa, block_num, block);
- }
- opline->op1_type = IS_UNUSED;
- opline->extended_value = 0;
- opline->opcode = ZEND_JMP;
- goto optimize_jmp;
- } else if (block->successors_count == 2) {
- if (block->successors[0] == block->successors[1]) {
- take_successor_0(ssa, block_num, block);
- if (block->successors[0] == next_block_num) {
- if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
- opline->opcode = ZEND_CHECK_VAR;
- opline->op2.num = 0;
- } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
- zend_ssa_remove_instr(ssa, opline, ssa_op);
- removed_ops++;
- goto optimize_nop;
- } else {
- opline->opcode = ZEND_FREE;
- opline->op2.num = 0;
- }
- } else if ((opline->op1_type == IS_CV && !(OP1_INFO() & MAY_BE_UNDEF)) || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
- ZEND_ASSERT(ssa_op->op1_use >= 0);
- zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
- ssa_op->op1_use = -1;
- ssa_op->op1_use_chain = -1;
- opline->opcode = ZEND_JMP;
- opline->op1_type = IS_UNUSED;
- opline->op1.num = opline->op2.num;
- goto optimize_jmp;
- }
- }
- }
- break;
- case ZEND_JMPZ_EX:
- if (ssa->vars[ssa_op->result_def].use_chain < 0
- && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
- opline->opcode = ZEND_JMPZ;
- opline->result_type = IS_UNUSED;
- zend_ssa_remove_result_def(ssa, ssa_op);
- goto optimize_jmpz;
- } else if (opline->op1_type == IS_CONST) {
- if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
- opline->opcode = ZEND_QM_ASSIGN;
- take_successor_1(ssa, block_num, block);
- }
- }
- break;
- case ZEND_JMPNZ_EX:
- if (ssa->vars[ssa_op->result_def].use_chain < 0
- && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
- opline->opcode = ZEND_JMPNZ;
- opline->result_type = IS_UNUSED;
- zend_ssa_remove_result_def(ssa, ssa_op);
- goto optimize_jmpnz;
- } else if (opline->op1_type == IS_CONST) {
- if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
- opline->opcode = ZEND_QM_ASSIGN;
- take_successor_1(ssa, block_num, block);
- }
- }
- break;
- case ZEND_JMP_SET:
- if (ssa->vars[ssa_op->result_def].use_chain < 0
- && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
- opline->opcode = ZEND_JMPNZ;
- opline->result_type = IS_UNUSED;
- zend_ssa_remove_result_def(ssa, ssa_op);
- goto optimize_jmpnz;
- } else if (opline->op1_type == IS_CONST) {
- if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
- MAKE_NOP(opline);
- removed_ops++;
- take_successor_1(ssa, block_num, block);
- zend_ssa_remove_result_def(ssa, ssa_op);
- goto optimize_nop;
- }
- }
- break;
- case ZEND_COALESCE:
- {
- zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
- if (opline->op1_type == IS_CONST
- && var->use_chain < 0 && var->phi_use_chain == NULL) {
- if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) {
- zend_ssa_remove_result_def(ssa, ssa_op);
- MAKE_NOP(opline);
- removed_ops++;
- take_successor_1(ssa, block_num, block);
- goto optimize_nop;
- } else {
- opline->opcode = ZEND_JMP;
- opline->result_type = IS_UNUSED;
- zend_ssa_remove_result_def(ssa, ssa_op);
- COPY_NODE(opline->op1, opline->op2);
- take_successor_0(ssa, block_num, block);
- goto optimize_jmp;
- }
- }
- break;
- }
- case ZEND_JMP_NULL:
- {
- zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
- if (opline->op1_type == IS_CONST
- && var->use_chain < 0 && var->phi_use_chain == NULL) {
- if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) {
- opline->opcode = ZEND_JMP;
- opline->result_type = IS_UNUSED;
- zend_ssa_remove_result_def(ssa, ssa_op);
- COPY_NODE(opline->op1, opline->op2);
- take_successor_0(ssa, block_num, block);
- goto optimize_jmp;
- } else {
- zend_ssa_remove_result_def(ssa, ssa_op);
- MAKE_NOP(opline);
- removed_ops++;
- take_successor_1(ssa, block_num, block);
- goto optimize_nop;
- }
- }
- break;
- }
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- if (opline->op1_type == IS_CONST) {
- zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
- zend_uchar type = Z_TYPE_P(zv);
- zend_bool correct_type =
- (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
- || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
- || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
-
- if (!correct_type) {
- removed_ops++;
- MAKE_NOP(opline);
- opline->extended_value = 0;
- take_successor_ex(ssa, block_num, block, block->successors[block->successors_count - 1]);
- goto optimize_nop;
- } else {
- HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
- zval *jmp_zv = type == IS_LONG
- ? zend_hash_index_find(jmptable, Z_LVAL_P(zv))
- : zend_hash_find(jmptable, Z_STR_P(zv));
-
- uint32_t target;
- if (jmp_zv) {
- target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv));
- } else {
- target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
- }
- opline->opcode = ZEND_JMP;
- opline->extended_value = 0;
- SET_UNUSED(opline->op1);
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target);
- SET_UNUSED(opline->op2);
- take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]);
- goto optimize_jmp;
- }
- }
- break;
- case ZEND_NOP:
-optimize_nop:
- compress_block(op_array, block);
- if (block->len == 0) {
- if (block_num > 0) {
- zend_ssa_unlink_block(op_array, ssa, block, block_num);
- /* backtrack to previous basic block */
- do {
- block_num--;
- } while (block_num >= 0
- && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE));
- if (block_num >= 0) {
- continue;
- }
- }
- }
- break;
- default:
- break;
- }
- }
-
- block_num = next_block_num;
- }
-
- return removed_ops;
-}
-
-static int zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ssa, int def, int cv_var)
-{
- int result_var = ssa->ops[def].result_def;
- int cv = EX_NUM_TO_VAR(ssa->vars[cv_var].var);
-
- if (result_var >= 0
- && !(ssa->var_info[cv_var].type & MAY_BE_REF)
- && ssa->vars[cv_var].alias == NO_ALIAS
- && ssa->vars[result_var].phi_use_chain == NULL
- && ssa->vars[result_var].sym_use_chain == NULL) {
- int use = ssa->vars[result_var].use_chain;
-
- if (use >= 0
- && zend_ssa_next_use(ssa->ops, result_var, use) < 0
- && op_array->opcodes[use].opcode != ZEND_FREE
- && op_array->opcodes[use].opcode != ZEND_SEND_VAL
- && op_array->opcodes[use].opcode != ZEND_SEND_VAL_EX
- && op_array->opcodes[use].opcode != ZEND_VERIFY_RETURN_TYPE) {
- if (use > def) {
- int i = use;
- const zend_op *opline = &op_array->opcodes[use];
-
- while (i > def) {
- if ((opline->op1_type == IS_CV && opline->op1.var == cv)
- || (opline->op2_type == IS_CV && opline->op2.var == cv)
- || (opline->result_type == IS_CV && opline->result.var == cv)) {
- return 0;
- }
- opline--;
- i--;
- }
-
- /* Update opcodes and reconstruct SSA */
- ssa->vars[result_var].definition = -1;
- ssa->vars[result_var].use_chain = -1;
- ssa->ops[def].result_def = -1;
-
- op_array->opcodes[def].result_type = IS_UNUSED;
- op_array->opcodes[def].result.var = 0;
-
- if (ssa->ops[use].op1_use == result_var) {
- ssa->ops[use].op1_use = cv_var;
- ssa->ops[use].op1_use_chain = ssa->vars[cv_var].use_chain;
- ssa->vars[cv_var].use_chain = use;
-
- op_array->opcodes[use].op1_type = IS_CV;
- op_array->opcodes[use].op1.var = cv;
- } else if (ssa->ops[use].op2_use == result_var) {
- ssa->ops[use].op2_use = cv_var;
- ssa->ops[use].op2_use_chain = ssa->vars[cv_var].use_chain;
- ssa->vars[cv_var].use_chain = use;
-
- op_array->opcodes[use].op2_type = IS_CV;
- op_array->opcodes[use].op2.var = cv;
- } else if (ssa->ops[use].result_use == result_var) {
- ssa->ops[use].result_use = cv_var;
- ssa->ops[use].res_use_chain = ssa->vars[cv_var].use_chain;
- ssa->vars[cv_var].use_chain = use;
-
- op_array->opcodes[use].result_type = IS_CV;
- op_array->opcodes[use].result.var = cv;
- }
-
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
-{
- if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
- zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
- }
-
- if (ssa->var_info) {
- int op_1;
- int v;
- int remove_nops = 0;
- zend_op *opline;
- zend_ssa_op *ssa_op;
- zval tmp;
-
-#if ZEND_DEBUG_DFA
- ssa_verify_integrity(op_array, ssa, "before dfa");
-#endif
-
- if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
- if (sccp_optimize_op_array(ctx, op_array, ssa, call_map)) {
- remove_nops = 1;
- }
-
- if (zend_dfa_optimize_jmps(op_array, ssa)) {
- remove_nops = 1;
- }
-
-#if ZEND_DEBUG_DFA
- ssa_verify_integrity(op_array, ssa, "after sccp");
-#endif
- if (ZEND_FUNC_INFO(op_array)) {
- if (zend_dfa_optimize_calls(op_array, ssa)) {
- remove_nops = 1;
- }
- }
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_8) {
- zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after sccp pass", ssa);
- }
-#if ZEND_DEBUG_DFA
- ssa_verify_integrity(op_array, ssa, "after calls");
-#endif
- }
-
- if (ZEND_OPTIMIZER_PASS_14 & ctx->optimization_level) {
- if (dce_optimize_op_array(op_array, ssa, 0)) {
- remove_nops = 1;
- }
- if (zend_dfa_optimize_jmps(op_array, ssa)) {
- remove_nops = 1;
- }
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_14) {
- zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dce pass", ssa);
- }
-#if ZEND_DEBUG_DFA
- ssa_verify_integrity(op_array, ssa, "after dce");
-#endif
- }
-
- for (v = op_array->last_var; v < ssa->vars_count; v++) {
-
- op_1 = ssa->vars[v].definition;
-
- if (op_1 < 0) {
- continue;
- }
-
- opline = op_array->opcodes + op_1;
- ssa_op = &ssa->ops[op_1];
-
- /* Convert LONG constants to DOUBLE */
- if (ssa->var_info[v].use_as_double) {
- if (opline->opcode == ZEND_ASSIGN
- && opline->op2_type == IS_CONST
- && ssa->ops[op_1].op1_def == v
- && !RETURN_VALUE_USED(opline)
- ) {
-
-// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
-
- zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
- ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
-
- } else if (opline->opcode == ZEND_QM_ASSIGN
- && opline->op1_type == IS_CONST
- ) {
-
-// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
-
- zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
- ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
- }
-
- } else {
- if (opline->opcode == ZEND_ADD
- || opline->opcode == ZEND_SUB
- || opline->opcode == ZEND_MUL
- || opline->opcode == ZEND_IS_EQUAL
- || opline->opcode == ZEND_IS_NOT_EQUAL
- || opline->opcode == ZEND_IS_SMALLER
- || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
- ) {
-
- if (opline->op1_type == IS_CONST && opline->op2_type != IS_CONST) {
- zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
-
- if ((OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
- && Z_TYPE_INFO_P(zv) == IS_LONG) {
-
-// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
-
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
- zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
- }
- if (opline->opcode == ZEND_ADD) {
- zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
-
- if (((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
- && Z_TYPE_INFO_P(zv) == IS_LONG
- && Z_LVAL_P(zv) == 0)
- || ((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
- && Z_TYPE_INFO_P(zv) == IS_DOUBLE
- && Z_DVAL_P(zv) == 0.0)) {
-
-// op_1: #v.? = ADD 0, #?.? [double,long] => #v.? = QM_ASSIGN #?.?
-
- opline->opcode = ZEND_QM_ASSIGN;
- opline->op1_type = opline->op2_type;
- opline->op1.var = opline->op2.var;
- opline->op2_type = IS_UNUSED;
- opline->op2.num = 0;
- ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
- ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
- ssa->ops[op_1].op2_use = -1;
- ssa->ops[op_1].op2_use_chain = -1;
- }
- }
- } else if (opline->op1_type != IS_CONST && opline->op2_type == IS_CONST) {
- zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
-
- if ((OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
- && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG) {
-
-// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
-
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
- zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
- }
- if (opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB) {
- if (((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
- && Z_TYPE_INFO_P(zv) == IS_LONG
- && Z_LVAL_P(zv) == 0)
- || ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
- && Z_TYPE_INFO_P(zv) == IS_DOUBLE
- && Z_DVAL_P(zv) == 0.0)) {
-
-// op_1: #v.? = ADD #?.? [double,long], 0 => #v.? = QM_ASSIGN #?.?
-
- opline->opcode = ZEND_QM_ASSIGN;
- opline->op2_type = IS_UNUSED;
- opline->op2.num = 0;
- }
- }
- }
- } else if (opline->opcode == ZEND_CONCAT) {
- if (!(OP1_INFO() & MAY_BE_OBJECT)
- && !(OP2_INFO() & MAY_BE_OBJECT)) {
- opline->opcode = ZEND_FAST_CONCAT;
- }
- } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
- && opline->op1_type != IS_CONST
- && ssa->ops[op_1].op1_def == v
- && ssa->ops[op_1].op1_use >= 0
- && ssa->ops[op_1].op1_use_chain == -1
- && ssa->vars[v].use_chain >= 0
- && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) {
-
-// op_1: VERIFY_RETURN_TYPE #orig_var.? [T] -> #v.? [T] => NOP
-
- int orig_var = ssa->ops[op_1].op1_use;
- if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
-
- int ret = ssa->vars[v].use_chain;
-
- ssa->ops[ret].op1_use = orig_var;
- ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain;
- ssa->vars[orig_var].use_chain = ret;
-
- ssa->vars[v].definition = -1;
- ssa->vars[v].use_chain = -1;
-
- ssa->ops[op_1].op1_def = -1;
- ssa->ops[op_1].op1_use = -1;
-
- MAKE_NOP(opline);
- remove_nops = 1;
- }
- }
- }
-
- if (opline->opcode == ZEND_QM_ASSIGN
- && ssa->ops[op_1].result_def == v
- && opline->op1_type & (IS_TMP_VAR|IS_VAR)
- && !(ssa->var_info[v].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
- ) {
-
- int src_var = ssa->ops[op_1].op1_use;
-
- if (src_var >= 0
- && !(ssa->var_info[src_var].type & MAY_BE_REF)
- && ssa->vars[src_var].definition >= 0
- && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
- && ssa->ops[ssa->vars[src_var].definition].result_use < 0
- && ssa->vars[src_var].use_chain == op_1
- && ssa->ops[op_1].op1_use_chain < 0
- && !ssa->vars[src_var].phi_use_chain
- && !ssa->vars[src_var].sym_use_chain
- && opline_supports_assign_contraction(
- ssa, &op_array->opcodes[ssa->vars[src_var].definition],
- src_var, opline->result.var)
- ) {
-
- int orig_var = ssa->ops[op_1].result_use;
- int op_2 = ssa->vars[src_var].definition;
-
-// op_2: #src_var.T = OP ... => #v.CV = OP ...
-// op_1: QM_ASSIGN #src_var.T #orig_var.CV [undef,scalar] -> #v.CV, NOP
-
- if (orig_var < 0 || zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
- /* Reconstruct SSA */
- ssa->vars[v].definition = op_2;
- ssa->ops[op_2].result_def = v;
-
- ssa->vars[src_var].definition = -1;
- ssa->vars[src_var].use_chain = -1;
-
- ssa->ops[op_1].op1_use = -1;
- ssa->ops[op_1].op1_def = -1;
- ssa->ops[op_1].op1_use_chain = -1;
- ssa->ops[op_1].result_use = -1;
- ssa->ops[op_1].result_def = -1;
- ssa->ops[op_1].res_use_chain = -1;
-
- /* Update opcodes */
- op_array->opcodes[op_2].result_type = opline->result_type;
- op_array->opcodes[op_2].result.var = opline->result.var;
-
- MAKE_NOP(opline);
- remove_nops = 1;
-
- if (op_array->opcodes[op_2].opcode == ZEND_SUB
- && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
- && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
- && op_array->opcodes[op_2].op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
- && ssa->ops[op_2].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
- SET_UNUSED(op_array->opcodes[op_2].op2);
- SET_UNUSED(op_array->opcodes[op_2].result);
-
- ssa->ops[op_2].result_def = -1;
- ssa->ops[op_2].op1_def = v;
-
- } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
- && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
- && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
- && op_array->opcodes[op_2].op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
- && ssa->ops[op_2].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
- SET_UNUSED(op_array->opcodes[op_2].op2);
- SET_UNUSED(op_array->opcodes[op_2].result);
-
- ssa->ops[op_2].result_def = -1;
- ssa->ops[op_2].op1_def = v;
-
- } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
- && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type
- && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var
- && op_array->opcodes[op_2].op1_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1
- && ssa->ops[op_2].op2_use >= 0
- && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
- op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type;
- op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var;
- SET_UNUSED(op_array->opcodes[op_2].op2);
- SET_UNUSED(op_array->opcodes[op_2].result);
-
- ssa->ops[op_2].result_def = -1;
- ssa->ops[op_2].op1_def = v;
- ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use;
- ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain;
- ssa->ops[op_2].op2_use = -1;
- ssa->ops[op_2].op2_use_chain = -1;
- }
- }
- }
- }
-
- if (ssa->vars[v].var >= op_array->last_var) {
- /* skip TMP and VAR */
- continue;
- }
-
- if (ssa->ops[op_1].op1_def == v
- && RETURN_VALUE_USED(opline)) {
- if (opline->opcode == ZEND_ASSIGN
- || opline->opcode == ZEND_ASSIGN_OP
- || opline->opcode == ZEND_PRE_INC
- || opline->opcode == ZEND_PRE_DEC) {
- zend_dfa_try_to_replace_result(op_array, ssa, op_1, v);
- } else if (opline->opcode == ZEND_POST_INC) {
- int result_var = ssa->ops[op_1].result_def;
-
- if (result_var >= 0
- && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
- int use = ssa->vars[result_var].use_chain;
-
- if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER
- && ssa->ops[use].op1_use == result_var
- && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
- opline->opcode = ZEND_PRE_INC;
- op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
- }
- }
- } else if (opline->opcode == ZEND_POST_DEC) {
- int result_var = ssa->ops[op_1].result_def;
-
- if (result_var >= 0
- && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
- int use = ssa->vars[result_var].use_chain;
-
- if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER
- && ssa->ops[use].op2_use == result_var
- && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
- opline->opcode = ZEND_PRE_DEC;
- op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
- }
- }
- }
- }
-
- if (opline->opcode == ZEND_ASSIGN
- && ssa->ops[op_1].op1_def == v
- && !RETURN_VALUE_USED(opline)
- ) {
- int orig_var = ssa->ops[op_1].op1_use;
-
- if (orig_var >= 0
- && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
- ) {
- int src_var = ssa->ops[op_1].op2_use;
-
- if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
- && src_var >= 0
- && !(ssa->var_info[src_var].type & MAY_BE_REF)
- && ssa->vars[src_var].definition >= 0
- && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
- && ssa->ops[ssa->vars[src_var].definition].result_use < 0
- && ssa->vars[src_var].use_chain == op_1
- && ssa->ops[op_1].op2_use_chain < 0
- && !ssa->vars[src_var].phi_use_chain
- && !ssa->vars[src_var].sym_use_chain
- && opline_supports_assign_contraction(
- ssa, &op_array->opcodes[ssa->vars[src_var].definition],
- src_var, opline->op1.var)
- ) {
-
- int op_2 = ssa->vars[src_var].definition;
-
-// op_2: #src_var.T = OP ... => #v.CV = OP ...
-// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T NOP
-
- if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
- /* Reconstruct SSA */
- ssa->vars[v].definition = op_2;
- ssa->ops[op_2].result_def = v;
-
- ssa->vars[src_var].definition = -1;
- ssa->vars[src_var].use_chain = -1;
-
- ssa->ops[op_1].op1_use = -1;
- ssa->ops[op_1].op2_use = -1;
- ssa->ops[op_1].op1_def = -1;
- ssa->ops[op_1].op1_use_chain = -1;
-
- /* Update opcodes */
- op_array->opcodes[op_2].result_type = opline->op1_type;
- op_array->opcodes[op_2].result.var = opline->op1.var;
-
- MAKE_NOP(opline);
- remove_nops = 1;
-
- if (op_array->opcodes[op_2].opcode == ZEND_SUB
- && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
- && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
- && op_array->opcodes[op_2].op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
- && ssa->ops[op_2].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
- SET_UNUSED(op_array->opcodes[op_2].op2);
- SET_UNUSED(op_array->opcodes[op_2].result);
-
- ssa->ops[op_2].result_def = -1;
- ssa->ops[op_2].op1_def = v;
-
- } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
- && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
- && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
- && op_array->opcodes[op_2].op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
- && ssa->ops[op_2].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
- SET_UNUSED(op_array->opcodes[op_2].op2);
- SET_UNUSED(op_array->opcodes[op_2].result);
-
- ssa->ops[op_2].result_def = -1;
- ssa->ops[op_2].op1_def = v;
-
- } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
- && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type
- && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var
- && op_array->opcodes[op_2].op1_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1
- && ssa->ops[op_2].op2_use >= 0
- && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
- op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
- op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type;
- op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var;
- SET_UNUSED(op_array->opcodes[op_2].op2);
- SET_UNUSED(op_array->opcodes[op_2].result);
-
- ssa->ops[op_2].result_def = -1;
- ssa->ops[op_2].op1_def = v;
- ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use;
- ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain;
- ssa->ops[op_2].op2_use = -1;
- ssa->ops[op_2].op2_use_chain = -1;
- }
- }
- } else if (opline->op2_type == IS_CONST
- || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
- && ssa->ops[op_1].op2_use >= 0
- && ssa->ops[op_1].op2_def < 0)
- ) {
-
-// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
-
- if (ssa->ops[op_1].op1_use != ssa->ops[op_1].op2_use) {
- zend_ssa_unlink_use_chain(ssa, op_1, orig_var);
- } else {
- ssa->ops[op_1].op2_use_chain = ssa->ops[op_1].op1_use_chain;
- }
-
- /* Reconstruct SSA */
- ssa->ops[op_1].result_def = v;
- ssa->ops[op_1].op1_def = -1;
- ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
- ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
- ssa->ops[op_1].op2_use = -1;
- ssa->ops[op_1].op2_use_chain = -1;
-
- /* Update opcode */
- opline->result_type = opline->op1_type;
- opline->result.var = opline->op1.var;
- opline->op1_type = opline->op2_type;
- opline->op1.var = opline->op2.var;
- opline->op2_type = IS_UNUSED;
- opline->op2.var = 0;
- opline->opcode = ZEND_QM_ASSIGN;
- }
- }
-
- } else if (opline->opcode == ZEND_ASSIGN_OP
- && opline->extended_value == ZEND_ADD
- && ssa->ops[op_1].op1_def == v
- && opline->op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
- && ssa->ops[op_1].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
-// op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV
-
- opline->opcode = ZEND_PRE_INC;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
-
- } else if (opline->opcode == ZEND_ASSIGN_OP
- && opline->extended_value == ZEND_SUB
- && ssa->ops[op_1].op1_def == v
- && opline->op2_type == IS_CONST
- && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
- && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
- && ssa->ops[op_1].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
-
-// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV
-
- opline->opcode = ZEND_PRE_DEC;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
-
- } else if (ssa->ops[op_1].op1_def == v
- && !RETURN_VALUE_USED(opline)
- && ssa->ops[op_1].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
- && opline->opcode == ZEND_ASSIGN_OP
- && opline->extended_value != ZEND_CONCAT) {
-
-// op_1: ASSIGN_OP #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ?
-
- /* Reconstruct SSA */
- ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
- ssa->ops[op_1].op1_def = -1;
-
- /* Update opcode */
- opline->opcode = opline->extended_value;
- opline->extended_value = 0;
- opline->result_type = opline->op1_type;
- opline->result.var = opline->op1.var;
-
- }
- }
-
-#if ZEND_DEBUG_DFA
- ssa_verify_integrity(op_array, ssa, "after dfa");
-#endif
-
- if (remove_nops) {
- zend_ssa_remove_nops(op_array, ssa, ctx);
-#if ZEND_DEBUG_DFA
- ssa_verify_integrity(op_array, ssa, "after nop");
-#endif
- }
- }
-
- if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
- zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
- }
-}
-
-void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- void *checkpoint = zend_arena_checkpoint(ctx->arena);
- zend_ssa ssa;
-
- if (zend_dfa_analyze_op_array(op_array, ctx, &ssa) != SUCCESS) {
- zend_arena_release(&ctx->arena, checkpoint);
- return;
- }
-
- zend_dfa_optimize_op_array(op_array, ctx, &ssa, NULL);
-
- /* Destroy SSA */
- zend_arena_release(&ctx->arena, checkpoint);
-}
diff --git a/ext/opcache/Optimizer/escape_analysis.c b/ext/opcache/Optimizer/escape_analysis.c
deleted file mode 100644
index a5577d5995..0000000000
--- a/ext/opcache/Optimizer/escape_analysis.c
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache, Escape Analysis |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_bitset.h"
-#include "zend_cfg.h"
-#include "zend_ssa.h"
-#include "zend_inference.h"
-#include "zend_dump.h"
-
-/*
- * T. Kotzmann and H. Mossenbock. Escape analysis in the context of dynamic
- * compilation and deoptimization. In Proceedings of the International
- * Conference on Virtual Execution Environments, pages 111-120, Chicago,
- * June 2005
- */
-
-static zend_always_inline void union_find_init(int *parent, int *size, int count) /* {{{ */
-{
- int i;
-
- for (i = 0; i < count; i++) {
- parent[i] = i;
- size[i] = 1;
- }
-}
-/* }}} */
-
-static zend_always_inline int union_find_root(int *parent, int i) /* {{{ */
-{
- int p = parent[i];
-
- while (i != p) {
- p = parent[p];
- parent[i] = p;
- i = p;
- p = parent[i];
- }
- return i;
-}
-/* }}} */
-
-static zend_always_inline void union_find_unite(int *parent, int *size, int i, int j) /* {{{ */
-{
- int r1 = union_find_root(parent, i);
- int r2 = union_find_root(parent, j);
-
- if (r1 != r2) {
- if (size[r1] < size[r2]) {
- parent[r1] = r2;
- size[r2] += size[r1];
- } else {
- parent[r2] = r1;
- size[r1] += size[r2];
- }
- }
-}
-/* }}} */
-
-static int zend_build_equi_escape_sets(int *parent, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
-{
- zend_ssa_var *ssa_vars = ssa->vars;
- int ssa_vars_count = ssa->vars_count;
- zend_ssa_phi *p;
- int i, j;
- int *size;
- ALLOCA_FLAG(use_heap)
-
- size = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
- if (!size) {
- return FAILURE;
- }
- union_find_init(parent, size, ssa_vars_count);
-
- for (i = 0; i < ssa_vars_count; i++) {
- if (ssa_vars[i].definition_phi) {
- p = ssa_vars[i].definition_phi;
- if (p->pi >= 0) {
- union_find_unite(parent, size, i, p->sources[0]);
- } else {
- for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
- union_find_unite(parent, size, i, p->sources[j]);
- }
- }
- } else if (ssa_vars[i].definition >= 0) {
- int def = ssa_vars[i].definition;
- zend_ssa_op *op = ssa->ops + def;
- zend_op *opline = op_array->opcodes + def;
-
- if (op->op1_def >= 0) {
- if (op->op1_use >= 0) {
- if (opline->opcode != ZEND_ASSIGN) {
- union_find_unite(parent, size, op->op1_def, op->op1_use);
- }
- }
- if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
- union_find_unite(parent, size, op->op1_def, op->op2_use);
- }
- }
- if (op->op2_def >= 0) {
- if (op->op2_use >= 0) {
- union_find_unite(parent, size, op->op2_def, op->op2_use);
- }
- }
- if (op->result_def >= 0) {
- if (op->result_use >= 0) {
- if (opline->opcode != ZEND_QM_ASSIGN) {
- union_find_unite(parent, size, op->result_def, op->result_use);
- }
- }
- if (opline->opcode == ZEND_QM_ASSIGN && op->op1_use >= 0) {
- union_find_unite(parent, size, op->result_def, op->op1_use);
- }
- if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
- union_find_unite(parent, size, op->result_def, op->op2_use);
- }
- if (opline->opcode == ZEND_ASSIGN && op->op1_def >= 0) {
- union_find_unite(parent, size, op->result_def, op->op1_def);
- }
- }
- }
- }
-
- for (i = 0; i < ssa_vars_count; i++) {
- parent[i] = union_find_root(parent, i);
- }
-
- free_alloca(size, use_heap);
-
- return SUCCESS;
-}
-/* }}} */
-
-static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) /* {{{ */
-{
- zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
- if (ce) {
- return ce;
- }
-
- ce = zend_hash_find_ptr(CG(class_table), lcname);
- if (ce && ce->type == ZEND_INTERNAL_CLASS) {
- return ce;
- }
-
- return NULL;
-}
-/* }}} */
-
-static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
-{
- zend_ssa_op *ssa_op = ssa->ops + def;
- zend_op *opline = op_array->opcodes + def;
-
- if (ssa_op->result_def == var) {
- switch (opline->opcode) {
- case ZEND_INIT_ARRAY:
- return 1;
- case ZEND_NEW:
- /* objects with destructors should escape */
- if (opline->op1_type == IS_CONST) {
- zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
- uint32_t forbidden_flags =
- /* These flags will always cause an exception */
- ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
- | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
- if (ce && !ce->parent && !ce->create_object && !ce->constructor &&
- !ce->destructor && !ce->__get && !ce->__set &&
- !(ce->ce_flags & forbidden_flags) &&
- (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
- return 1;
- }
- }
- break;
- case ZEND_QM_ASSIGN:
- if (opline->op1_type == IS_CONST
- && Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) {
- return 1;
- }
- if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
- return 1;
- }
- break;
- case ZEND_ASSIGN:
- if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
- return 1;
- }
- break;
- }
- } else if (ssa_op->op1_def == var) {
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- if (opline->op2_type == IS_CONST
- && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) {
- return 1;
- }
- if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) {
- return 1;
- }
- break;
- case ZEND_ASSIGN_DIM:
- if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) {
- /* implicit object/array allocation */
- return 1;
- }
- break;
- }
- }
-
- return 0;
-}
-/* }}} */
-
-static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
-{
- zend_ssa_op *op = ssa->ops + def;
- zend_op *opline = op_array->opcodes + def;
-
- if (op->result_def == var) {
- switch (opline->opcode) {
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- case ZEND_QM_ASSIGN:
- case ZEND_ASSIGN:
- return 1;
- case ZEND_NEW:
- /* objects with destructors should escape */
- if (opline->op1_type == IS_CONST) {
- zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
- if (ce && !ce->create_object && !ce->constructor &&
- !ce->destructor && !ce->__get && !ce->__set && !ce->parent) {
- return 1;
- }
- }
- break;
- }
- } else if (op->op1_def == var) {
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- return 1;
- }
- }
-
- return 0;
-}
-/* }}} */
-
-static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int var) /* {{{ */
-{
- zend_ssa_op *ssa_op = ssa->ops + use;
- zend_op *opline = op_array->opcodes + use;
-
- if (ssa_op->op1_use == var) {
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- /* no_val */
- break;
- case ZEND_QM_ASSIGN:
- if (opline->op1_type == IS_CV) {
- if (OP1_INFO() & MAY_BE_OBJECT) {
- /* object aliasing */
- return 1;
- }
- }
- break;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_OBJ_IS:
- break;
- case ZEND_ASSIGN_OP:
- return 1;
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_REF:
- break;
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- break;
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
- return 1;
- }
- if (OP1_INFO() & MAY_BE_OBJECT) {
- /* object aliasing */
- return 1;
- }
- /* reference dependencies processed separately */
- break;
- case ZEND_OP_DATA:
- if ((opline-1)->opcode != ZEND_ASSIGN_DIM
- && (opline-1)->opcode != ZEND_ASSIGN_OBJ) {
- return 1;
- }
- if (OP1_INFO() & MAY_BE_OBJECT) {
- /* object aliasing */
- return 1;
- }
- opline--;
- ssa_op--;
- if (opline->op1_type != IS_CV
- || (OP1_INFO() & MAY_BE_REF)
- || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
- /* assignment into escaping structure */
- return 1;
- }
- /* reference dependencies processed separately */
- break;
- default:
- return 1;
- }
- }
-
- if (ssa_op->op2_use == var) {
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- if (opline->op1_type != IS_CV
- || (OP1_INFO() & MAY_BE_REF)
- || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
- /* assignment into escaping variable */
- return 1;
- }
- if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) {
- if (OP2_INFO() & MAY_BE_OBJECT) {
- /* object aliasing */
- return 1;
- }
- }
- break;
- default:
- return 1;
- }
- }
-
- if (ssa_op->result_use == var) {
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- case ZEND_QM_ASSIGN:
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- break;
- default:
- return 1;
- }
- }
-
- return 0;
-}
-/* }}} */
-
-int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
-{
- zend_ssa_var *ssa_vars = ssa->vars;
- int ssa_vars_count = ssa->vars_count;
- int i, root, use;
- int *ees;
- zend_bool has_allocations;
- int num_non_escaped;
- ALLOCA_FLAG(use_heap)
-
- if (!ssa_vars) {
- return SUCCESS;
- }
-
- has_allocations = 0;
- for (i = op_array->last_var; i < ssa_vars_count; i++) {
- if (ssa_vars[i].definition >= 0
- && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))
- && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
- has_allocations = 1;
- break;
- }
- }
- if (!has_allocations) {
- return SUCCESS;
- }
-
-
- /* 1. Build EES (Equi-Escape Sets) */
- ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
- if (!ees) {
- return FAILURE;
- }
-
- if (zend_build_equi_escape_sets(ees, op_array, ssa) != SUCCESS) {
- return FAILURE;
- }
-
- /* 2. Identify Allocations */
- num_non_escaped = 0;
- for (i = op_array->last_var; i < ssa_vars_count; i++) {
- root = ees[i];
- if (ssa_vars[root].escape_state > ESCAPE_STATE_NO_ESCAPE) {
- /* already escape. skip */
- } else if (ssa_vars[i].alias && (ssa->var_info[i].type & MAY_BE_REF)) {
- if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- num_non_escaped--;
- }
- ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
- } else if (ssa_vars[i].definition >= 0
- && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
- if (!is_local_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
- if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- num_non_escaped--;
- }
- ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
- } else if (ssa_vars[root].escape_state == ESCAPE_STATE_UNKNOWN
- && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
- ssa_vars[root].escape_state = ESCAPE_STATE_NO_ESCAPE;
- num_non_escaped++;
- }
- }
- }
-
- /* 3. Mark escaped EES */
- if (num_non_escaped) {
- for (i = 0; i < ssa_vars_count; i++) {
- if (ssa_vars[i].use_chain >= 0) {
- root = ees[i];
- if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- FOREACH_USE(ssa_vars + i, use) {
- if (is_escape_use(op_array, ssa, use, i)) {
- ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
- num_non_escaped--;
- if (num_non_escaped == 0) {
- i = ssa_vars_count;
- }
- break;
- }
- } FOREACH_USE_END();
- }
- }
- }
- }
-
- /* 4. Process referential dependencies */
- if (num_non_escaped) {
- zend_bool changed;
-
- do {
- changed = 0;
- for (i = 0; i < ssa_vars_count; i++) {
- if (ssa_vars[i].use_chain >= 0) {
- root = ees[i];
- if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- FOREACH_USE(ssa_vars + i, use) {
- zend_ssa_op *op = ssa->ops + use;
- zend_op *opline = op_array->opcodes + use;
- int enclosing_root;
-
- if (opline->opcode == ZEND_OP_DATA &&
- ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
- (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
- (opline-1)->opcode == ZEND_ASSIGN_OBJ_REF) &&
- op->op1_use == i &&
- (op-1)->op1_use >= 0) {
- enclosing_root = ees[(op-1)->op1_use];
- } else if ((opline->opcode == ZEND_INIT_ARRAY ||
- opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
- op->op1_use == i &&
- op->result_def >= 0) {
- enclosing_root = ees[op->result_def];
- } else {
- continue;
- }
-
- if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN ||
- ssa_vars[enclosing_root].escape_state > ssa_vars[root].escape_state) {
- if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN) {
- ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
- } else {
- ssa_vars[root].escape_state = ssa_vars[enclosing_root].escape_state;
- }
- if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) {
- num_non_escaped--;
- if (num_non_escaped == 0) {
- changed = 0;
- } else {
- changed = 1;
- }
- break;
- } else {
- changed = 1;
- }
- }
- } FOREACH_USE_END();
- }
- }
- }
- } while (changed);
- }
-
- /* 5. Propagate values of escape sets to variables */
- for (i = 0; i < ssa_vars_count; i++) {
- root = ees[i];
- if (i != root) {
- ssa_vars[i].escape_state = ssa_vars[root].escape_state;
- }
- }
-
- free_alloca(ees, use_heap);
-
- return SUCCESS;
-}
-/* }}} */
diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c
deleted file mode 100644
index 32d2f10bf4..0000000000
--- a/ext/opcache/Optimizer/nop_removal.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-/* pass 10:
- * - remove NOPs
- */
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-
-void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_op *end, *opline;
- uint32_t new_count, i, shift;
- int j;
- uint32_t *shiftlist;
- ALLOCA_FLAG(use_heap);
-
- shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
- i = new_count = shift = 0;
- end = op_array->opcodes + op_array->last;
- for (opline = op_array->opcodes; opline < end; opline++) {
-
- /* Kill JMP-over-NOP-s */
- if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
- /* check if there are only NOPs under the branch */
- zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
-
- while (target->opcode == ZEND_NOP) {
- target--;
- }
- if (target == opline) {
- /* only NOPs */
- opline->opcode = ZEND_NOP;
- }
- }
-
- shiftlist[i++] = shift;
- if (opline->opcode == ZEND_NOP) {
- shift++;
- } else {
- if (shift) {
- zend_op *new_opline = op_array->opcodes + new_count;
-
- *new_opline = *opline;
- zend_optimizer_migrate_jump(op_array, new_opline, opline);
- }
- new_count++;
- }
- }
-
- if (shift) {
- op_array->last = new_count;
- end = op_array->opcodes + op_array->last;
-
- /* update JMPs */
- for (opline = op_array->opcodes; opline<end; opline++) {
- zend_optimizer_shift_jump(op_array, opline, shiftlist);
- }
-
- /* update try/catch array */
- for (j = 0; j < op_array->last_try_catch; j++) {
- op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
- op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
- if (op_array->try_catch_array[j].finally_op) {
- op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
- op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
- }
- }
-
- /* update early binding list */
- if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
- uint32_t *opline_num = &ctx->script->first_early_binding_opline;
-
- ZEND_ASSERT(op_array == &ctx->script->main_op_array);
- do {
- *opline_num -= shiftlist[*opline_num];
- opline_num = &op_array->opcodes[*opline_num].result.opline_num;
- } while (*opline_num != (uint32_t)-1);
- }
- }
- free_alloca(shiftlist, use_heap);
-}
diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c
deleted file mode 100644
index b1d8f6b929..0000000000
--- a/ext/opcache/Optimizer/optimize_func_calls.c
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- | Xinchen Hui <laruence@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-/* pass 4
- * - optimize INIT_FCALL_BY_NAME to DO_FCALL
- */
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-
-#define ZEND_OP1_IS_CONST_STRING(opline) \
- (opline->op1_type == IS_CONST && \
- Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
-#define ZEND_OP2_IS_CONST_STRING(opline) \
- (opline->op2_type == IS_CONST && \
- Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
-
-typedef struct _optimizer_call_info {
- zend_function *func;
- zend_op *opline;
- zend_bool is_prototype;
- zend_bool try_inline;
- uint32_t func_arg_num;
-} optimizer_call_info;
-
-static void zend_delete_call_instructions(zend_op *opline)
-{
- int call = 0;
-
- while (1) {
- switch (opline->opcode) {
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_STATIC_METHOD_CALL:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_FCALL:
- if (call == 0) {
- MAKE_NOP(opline);
- return;
- }
- /* break missing intentionally */
- case ZEND_NEW:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_INIT_USER_CALL:
- call--;
- break;
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- call++;
- break;
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- if (call == 0) {
- if (opline->op1_type == IS_CONST) {
- MAKE_NOP(opline);
- } else if (opline->op1_type == IS_CV) {
- opline->opcode = ZEND_CHECK_VAR;
- opline->extended_value = 0;
- opline->result.var = 0;
- } else {
- opline->opcode = ZEND_FREE;
- opline->extended_value = 0;
- opline->result.var = 0;
- }
- }
- break;
- }
- opline--;
- }
-}
-
-static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
-{
- if (func->type == ZEND_USER_FUNCTION
- && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
- /* TODO: function copied from trait may be inconsistent ??? */
- && !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE))
- && fcall->extended_value >= func->op_array.required_num_args
- && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {
-
- zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
-
- if (ret_opline->op1_type == IS_CONST) {
- uint32_t i, num_args = func->op_array.num_args;
- num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
-
- if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
- && !(func->op_array.fn_flags & ZEND_ACC_STATIC)) {
- /* Don't inline static call to instance method. */
- return;
- }
-
- for (i = 0; i < num_args; i++) {
- /* Don't inline functions with by-reference arguments. This would require
- * correct handling of INDIRECT arguments. */
- if (ZEND_ARG_SEND_MODE(&func->op_array.arg_info[i])) {
- return;
- }
- }
-
- if (fcall->extended_value < func->op_array.num_args) {
- /* don't inline functions with named constants in default arguments */
- i = fcall->extended_value;
-
- do {
- if (Z_TYPE_P(CRT_CONSTANT_EX(&func->op_array, &func->op_array.opcodes[i], func->op_array.opcodes[i].op2)) == IS_CONSTANT_AST) {
- return;
- }
- i++;
- } while (i < func->op_array.num_args);
- }
-
- if (RETURN_VALUE_USED(opline)) {
- zval zv;
-
- ZVAL_COPY(&zv, CRT_CONSTANT_EX(&func->op_array, ret_opline, ret_opline->op1));
- opline->opcode = ZEND_QM_ASSIGN;
- opline->op1_type = IS_CONST;
- opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
- SET_UNUSED(opline->op2);
- } else {
- MAKE_NOP(opline);
- }
-
- zend_delete_call_instructions(opline-1);
- }
- }
-}
-
-void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_op *opline = op_array->opcodes;
- zend_op *end = opline + op_array->last;
- int call = 0;
- void *checkpoint;
- optimizer_call_info *call_stack;
-
- if (op_array->last < 2) {
- return;
- }
-
- checkpoint = zend_arena_checkpoint(ctx->arena);
- call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info));
- while (opline < end) {
- switch (opline->opcode) {
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_STATIC_METHOD_CALL:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_FCALL:
- case ZEND_NEW:
- /* The argument passing optimizations are valid for prototypes as well,
- * as inheritance cannot change between ref <-> non-ref arguments. */
- call_stack[call].func = zend_optimizer_get_called_func(
- ctx->script, op_array, opline, &call_stack[call].is_prototype);
- call_stack[call].try_inline =
- !call_stack[call].is_prototype && opline->opcode != ZEND_NEW;
- /* break missing intentionally */
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_INIT_USER_CALL:
- call_stack[call].opline = opline;
- call_stack[call].func_arg_num = (uint32_t)-1;
- call++;
- break;
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- call--;
- if (call_stack[call].func && call_stack[call].opline) {
- zend_op *fcall = call_stack[call].opline;
-
- if (fcall->opcode == ZEND_INIT_FCALL) {
- /* nothing to do */
- } else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
- fcall->opcode = ZEND_INIT_FCALL;
- fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
- literal_dtor(&ZEND_OP2_LITERAL(fcall));
- fcall->op2.constant = fcall->op2.constant + 1;
- opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
- } else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
- fcall->opcode = ZEND_INIT_FCALL;
- fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
- literal_dtor(&op_array->literals[fcall->op2.constant]);
- literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
- fcall->op2.constant = fcall->op2.constant + 1;
- opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
- } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
- || fcall->opcode == ZEND_INIT_METHOD_CALL
- || fcall->opcode == ZEND_NEW) {
- /* We don't have specialized opcodes for this, do nothing */
- } else {
- ZEND_UNREACHABLE();
- }
-
- if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
- && call_stack[call].try_inline) {
- zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
- }
- }
- call_stack[call].func = NULL;
- call_stack[call].opline = NULL;
- call_stack[call].try_inline = 0;
- call_stack[call].func_arg_num = (uint32_t)-1;
- break;
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_FETCH_DIM_FUNC_ARG:
- if (call_stack[call - 1].func
- && call_stack[call - 1].func_arg_num != (uint32_t)-1) {
- if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
- if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
- opline->opcode -= 9;
- } else {
- opline->opcode = ZEND_FETCH_STATIC_PROP_W;
- }
- } else {
- if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
- && opline->op2_type == IS_UNUSED) {
- /* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
- * Performing the replacement would create an invalid opcode. */
- call_stack[call - 1].try_inline = 0;
- break;
- }
-
- if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
- opline->opcode -= 12;
- } else {
- opline->opcode = ZEND_FETCH_STATIC_PROP_R;
- }
- }
- }
- break;
- case ZEND_SEND_VAL_EX:
- if (call_stack[call - 1].func) {
- if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
- break;
- }
-
- if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
- /* We won't convert it into_DO_FCALL to emit error at run-time */
- call_stack[call - 1].opline = NULL;
- } else {
- opline->opcode = ZEND_SEND_VAL;
- }
- }
- break;
- case ZEND_CHECK_FUNC_ARG:
- if (call_stack[call - 1].func) {
- if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
- call_stack[call - 1].func_arg_num = (uint32_t)-1;
- break;
- }
-
- call_stack[call - 1].func_arg_num = opline->op2.num;
- MAKE_NOP(opline);
- }
- break;
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- if (call_stack[call - 1].func) {
- if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
- break;
- }
-
- call_stack[call - 1].func_arg_num = (uint32_t)-1;
- if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
- opline->opcode = ZEND_SEND_REF;
- } else {
- opline->opcode = ZEND_SEND_VAR;
- }
- }
- break;
- case ZEND_SEND_VAR_NO_REF_EX:
- if (call_stack[call - 1].func) {
- if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
- break;
- }
-
- if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
- opline->opcode = ZEND_SEND_VAR_NO_REF;
- } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
- opline->opcode = ZEND_SEND_VAL;
- } else {
- opline->opcode = ZEND_SEND_VAR;
- }
- }
- break;
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- case ZEND_SEND_REF:
- if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
- break;
- }
- break;
- case ZEND_SEND_UNPACK:
- case ZEND_SEND_USER:
- case ZEND_SEND_ARRAY:
- call_stack[call - 1].try_inline = 0;
- break;
- default:
- break;
- }
- opline++;
- }
-
- zend_arena_release(&ctx->arena, checkpoint);
-}
diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c
deleted file mode 100644
index 6f7400159d..0000000000
--- a/ext/opcache/Optimizer/optimize_temp_vars_5.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-#include "zend_bitset.h"
-
-#define GET_AVAILABLE_T() \
- for (i = 0; i < T; i++) { \
- if (!zend_bitset_in(taken_T, i)) { \
- break; \
- } \
- } \
- zend_bitset_incl(taken_T, i); \
- if (i > max) { \
- max = i; \
- }
-
-void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- int T = op_array->T;
- int offset = op_array->last_var;
- uint32_t bitset_len;
- zend_bitset taken_T; /* T index in use */
- zend_op **start_of_T; /* opline where T is first used */
- zend_bitset valid_T; /* Is the map_T valid */
- int *map_T; /* Map's the T to its new index */
- zend_op *opline, *end;
- int currT;
- int i;
- int max = -1;
- void *checkpoint = zend_arena_checkpoint(ctx->arena);
-
- bitset_len = zend_bitset_len(T);
- taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
- start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
- valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
- map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
-
- end = op_array->opcodes;
- opline = &op_array->opcodes[op_array->last - 1];
-
- /* Find T definition points */
- while (opline >= end) {
- if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
- start_of_T[VAR_NUM(opline->result.var) - offset] = opline;
- }
- opline--;
- }
-
- zend_bitset_clear(valid_T, bitset_len);
- zend_bitset_clear(taken_T, bitset_len);
-
- end = op_array->opcodes;
- opline = &op_array->opcodes[op_array->last - 1];
-
- while (opline >= end) {
- if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) {
- currT = VAR_NUM(opline->op1.var) - offset;
- if (opline->opcode == ZEND_ROPE_END) {
- int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
- int var;
-
- var = max;
- while (var >= 0 && !zend_bitset_in(taken_T, var)) {
- var--;
- }
- max = MAX(max, var + num);
- var = var + 1;
- map_T[currT] = var;
- zend_bitset_incl(valid_T, currT);
- zend_bitset_incl(taken_T, var);
- opline->op1.var = NUM_VAR(var + offset);
- while (num > 1) {
- num--;
- zend_bitset_incl(taken_T, var + num);
- }
- } else {
- if (!zend_bitset_in(valid_T, currT)) {
- int use_new_var = 0;
-
- /* Code in "finally" blocks may modify temporary variables.
- * We allocate new temporaries for values that need to
- * relive FAST_CALLs.
- */
- if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
- (opline->opcode == ZEND_RETURN ||
- opline->opcode == ZEND_GENERATOR_RETURN ||
- opline->opcode == ZEND_RETURN_BY_REF ||
- opline->opcode == ZEND_FREE ||
- opline->opcode == ZEND_FE_FREE)) {
- zend_op *curr = opline;
-
- while (--curr >= end) {
- if (curr->opcode == ZEND_FAST_CALL) {
- use_new_var = 1;
- break;
- } else if (curr->opcode != ZEND_FREE &&
- curr->opcode != ZEND_FE_FREE &&
- curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
- curr->opcode != ZEND_DISCARD_EXCEPTION) {
- break;
- }
- }
- }
- if (use_new_var) {
- i = ++max;
- zend_bitset_incl(taken_T, i);
- } else {
- GET_AVAILABLE_T();
- }
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- }
- opline->op1.var = NUM_VAR(map_T[currT] + offset);
- }
- }
-
- if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) {
- currT = VAR_NUM(opline->op2.var) - offset;
- if (!zend_bitset_in(valid_T, currT)) {
- GET_AVAILABLE_T();
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- }
- opline->op2.var = NUM_VAR(map_T[currT] + offset);
- }
-
- if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
- currT = VAR_NUM(opline->result.var) - offset;
- if (zend_bitset_in(valid_T, currT)) {
- if (start_of_T[currT] == opline) {
- /* ZEND_FAST_CALL can not share temporary var with others
- * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
- * which could be ahead of it */
- if (opline->opcode != ZEND_FAST_CALL) {
- zend_bitset_excl(taken_T, map_T[currT]);
- }
- }
- opline->result.var = NUM_VAR(map_T[currT] + offset);
- if (opline->opcode == ZEND_ROPE_INIT) {
- if (start_of_T[currT] == opline) {
- uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
- while (num > 1) {
- num--;
- zend_bitset_excl(taken_T, map_T[currT]+num);
- }
- }
- }
- } else {
- /* Code which gets here is using a wrongly built opcode such as RECV() */
- GET_AVAILABLE_T();
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- opline->result.var = NUM_VAR(i + offset);
- }
- }
-
- opline--;
- }
-
- zend_arena_release(&ctx->arena, checkpoint);
- op_array->T = max + 1;
-}
diff --git a/ext/opcache/Optimizer/pass1.c b/ext/opcache/Optimizer/pass1.c
deleted file mode 100644
index 74f6153670..0000000000
--- a/ext/opcache/Optimizer/pass1.c
+++ /dev/null
@@ -1,685 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-/* pass 1 (Simple local optimizations)
- * - persistent constant substitution (true, false, null, etc)
- * - constant casting (ADD expects numbers, CONCAT strings, etc)
- * - constant expression evaluation
- * - optimize constant conditional JMPs
- * - pre-evaluate constant function calls
- * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM
- */
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-
-void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_op *opline = op_array->opcodes;
- zend_op *end = opline + op_array->last;
- zend_bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
- (op_array == &ctx->script->main_op_array) : 0;
-
- while (opline < end) {
- switch (opline->opcode) {
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- if (opline->op1_type == IS_CONST) {
- if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
- convert_to_string(&ZEND_OP1_LITERAL(opline));
- }
- }
- if (opline->op2_type == IS_CONST) {
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
- convert_to_string(&ZEND_OP2_LITERAL(opline));
- }
- if (opline->op1_type == IS_CONST) {
- goto constant_binary_op;
- }
- }
- break;
-
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- case ZEND_DIV:
- case ZEND_POW:
- case ZEND_MOD:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_BOOL_XOR:
- case ZEND_SPACESHIP:
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- if (opline->op1_type == IS_CONST &&
- opline->op2_type == IS_CONST) {
- /* binary operation with constant operands */
- zval result;
-
-constant_binary_op:
- if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &result);
- }
- }
- }
- break;
-
- case ZEND_ASSIGN_OP:
- if (opline->op2_type == IS_CONST) {
- if (opline->extended_value == ZEND_ADD
- || opline->extended_value == ZEND_SUB
- || opline->extended_value == ZEND_MUL
- || opline->extended_value == ZEND_DIV
- || opline->extended_value == ZEND_POW) {
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
- /* don't optimise if it should produce a runtime numeric string error */
- if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
- convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
- }
- }
- } else if (opline->extended_value == ZEND_MOD
- || opline->extended_value == ZEND_SL
- || opline->extended_value == ZEND_SR) {
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
- /* don't optimise if it should produce a runtime numeric string error */
- if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
- && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
- convert_to_long(&ZEND_OP2_LITERAL(opline));
- }
- }
- } else if (opline->extended_value == ZEND_CONCAT) {
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
- convert_to_string(&ZEND_OP2_LITERAL(opline));
- }
- }
- }
- break;
-
- case ZEND_CAST:
- if (opline->op1_type == IS_CONST) {
- /* cast of constant operand */
- zval result;
-
- if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- if (zend_optimizer_replace_by_const(op_array, opline + 1, opline->result_type, opline->result.var, &result)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- zend_optimizer_update_op1_const(op_array, opline, &result);
- }
- break;
- }
- }
- break;
-
- case ZEND_BW_NOT:
- case ZEND_BOOL_NOT:
- if (opline->op1_type == IS_CONST) {
- /* unary operation on constant operand */
- zval result;
-
- if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- zend_optimizer_update_op1_const(op_array, opline, &result);
- }
- }
- }
- break;
-
- case ZEND_FETCH_CONSTANT:
- if (opline->op2_type == IS_CONST &&
- Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
- Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 &&
- memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) {
- /* substitute __COMPILER_HALT_OFFSET__ constant */
- zend_execute_data *orig_execute_data = EG(current_execute_data);
- zend_execute_data fake_execute_data;
- zval *offset;
-
- memset(&fake_execute_data, 0, sizeof(zend_execute_data));
- fake_execute_data.func = (zend_function*)op_array;
- EG(current_execute_data) = &fake_execute_data;
- if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
-
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, offset)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, offset);
- }
- }
- EG(current_execute_data) = orig_execute_data;
- break;
- }
-
- if (opline->op2_type == IS_CONST &&
- Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
- /* substitute persistent constants */
- zval c;
-
- if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &c, 1)) {
- if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &c)) {
- break;
- }
- }
- if (Z_TYPE(c) == IS_CONSTANT_AST) {
- break;
- }
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &c);
- }
- }
- break;
-
- case ZEND_FETCH_CLASS_CONSTANT:
- if (opline->op2_type == IS_CONST &&
- Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
-
- zend_class_entry *ce = NULL;
-
- if (opline->op1_type == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
- /* for A::B */
- if (op_array->scope &&
- !strncasecmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)),
- ZSTR_VAL(op_array->scope->name), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1)) {
- ce = op_array->scope;
- } else {
- if ((ce = zend_hash_find_ptr(EG(class_table),
- Z_STR(op_array->literals[opline->op1.constant + 1]))) == NULL ||
- (ce->type == ZEND_INTERNAL_CLASS &&
- ce->info.internal.module->type != MODULE_PERSISTENT) ||
- (ce->type == ZEND_USER_CLASS &&
- ce->info.user.filename != op_array->filename)) {
- break;
- }
- }
- } else if (op_array->scope &&
- opline->op1_type == IS_UNUSED &&
- (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- /* for self::B */
- ce = op_array->scope;
- } else if (op_array->scope &&
- opline->op1_type == IS_VAR &&
- (opline - 1)->opcode == ZEND_FETCH_CLASS &&
- ((opline - 1)->op2_type == IS_UNUSED &&
- ((opline - 1)->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) &&
- (opline - 1)->result.var == opline->op1.var) {
- /* for self::B */
- ce = op_array->scope;
- }
-
- if (ce) {
- zend_class_constant *cc;
- zval *c, t;
-
- if ((cc = zend_hash_find_ptr(&ce->constants_table,
- Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL &&
- (Z_ACCESS_FLAGS(cc->value) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
- c = &cc->value;
- if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
- zend_ast *ast = Z_ASTVAL_P(c);
- if (ast->kind != ZEND_AST_CONSTANT
- || !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &t, 1)
- || Z_TYPE(t) == IS_CONSTANT_AST) {
- break;
- }
- } else {
- ZVAL_COPY_OR_DUP(&t, c);
- }
-
- if (opline->op1_type == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- } else if (opline->op1_type == IS_VAR) {
- MAKE_NOP((opline - 1));
- }
- literal_dtor(&ZEND_OP2_LITERAL(opline));
-
- if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &t)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &t);
- }
- }
- }
- }
- break;
-
- case ZEND_DO_ICALL: {
- zend_op *send1_opline = opline - 1;
- zend_op *send2_opline = NULL;
- zend_op *init_opline = NULL;
-
- while (send1_opline->opcode == ZEND_NOP) {
- send1_opline--;
- }
- if (send1_opline->opcode != ZEND_SEND_VAL ||
- send1_opline->op1_type != IS_CONST) {
- /* don't colllect constants after unknown function call */
- collect_constants = 0;
- break;
- }
- if (send1_opline->op2.num == 2) {
- send2_opline = send1_opline;
- send1_opline--;
- while (send1_opline->opcode == ZEND_NOP) {
- send1_opline--;
- }
- if (send1_opline->opcode != ZEND_SEND_VAL ||
- send1_opline->op1_type != IS_CONST) {
- /* don't colllect constants after unknown function call */
- collect_constants = 0;
- break;
- }
- }
- init_opline = send1_opline - 1;
- while (init_opline->opcode == ZEND_NOP) {
- init_opline--;
- }
- if (init_opline->opcode != ZEND_INIT_FCALL ||
- init_opline->op2_type != IS_CONST ||
- Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
- /* don't colllect constants after unknown function call */
- collect_constants = 0;
- break;
- }
-
- /* define("name", scalar); */
- if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("define")-1 &&
- zend_binary_strcasecmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), Z_STRLEN(ZEND_OP2_LITERAL(init_opline)), "define", sizeof("define")-1) == 0) {
-
- if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING &&
- send2_opline &&
- Z_TYPE(ZEND_OP1_LITERAL(send2_opline)) <= IS_STRING) {
-
- if (collect_constants) {
- zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline));
- }
-
- if (RESULT_UNUSED(opline) &&
- !zend_memnstr(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), "::", sizeof("::") - 1, Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)) + Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
-
- opline->opcode = ZEND_DECLARE_CONST;
- opline->op1_type = IS_CONST;
- opline->op2_type = IS_CONST;
- opline->result_type = IS_UNUSED;
- opline->op1.constant = send1_opline->op1.constant;
- opline->op2.constant = send2_opline->op1.constant;
- opline->result.num = 0;
-
- literal_dtor(&ZEND_OP2_LITERAL(init_opline));
- MAKE_NOP(init_opline);
- MAKE_NOP(send1_opline);
- MAKE_NOP(send2_opline);
- }
- break;
- }
- }
-
- /* pre-evaluate constant functions:
- constant(x)
- function_exists(x)
- is_callable(x)
- extension_loaded(x)
- */
- if (!send2_opline &&
- Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) {
- if ((Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("function_exists")-1 &&
- !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
- "function_exists", sizeof("function_exists")-1)) ||
- (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable")-1 &&
- !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
- "is_callable", sizeof("is_callable")))) {
- zend_internal_function *func;
- zend_string *lc_name = zend_string_tolower(
- Z_STR(ZEND_OP1_LITERAL(send1_opline)));
-
- if ((func = zend_hash_find_ptr(EG(function_table), lc_name)) != NULL
- && func->type == ZEND_INTERNAL_FUNCTION
- && func->module->type == MODULE_PERSISTENT
-#ifdef ZEND_WIN32
- && func->module->handle == NULL
-#endif
- ) {
- zval t;
- ZVAL_TRUE(&t);
- literal_dtor(&ZEND_OP2_LITERAL(init_opline));
- MAKE_NOP(init_opline);
- literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
- MAKE_NOP(send1_opline);
- if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &t);
- }
- }
- zend_string_release_ex(lc_name, 0);
- break;
- } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("extension_loaded")-1 &&
- !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
- "extension_loaded", sizeof("extension_loaded")-1)) {
- zval t;
- zend_string *lc_name = zend_string_tolower(
- Z_STR(ZEND_OP1_LITERAL(send1_opline)));
- zend_module_entry *m = zend_hash_find_ptr(&module_registry,
- lc_name);
-
- zend_string_release_ex(lc_name, 0);
- if (!m) {
- if (PG(enable_dl)) {
- break;
- } else {
- ZVAL_FALSE(&t);
- }
- } else {
- if (m->type == MODULE_PERSISTENT
-#ifdef ZEND_WIN32
- && m->handle == NULL
-#endif
- ) {
- ZVAL_TRUE(&t);
- } else {
- break;
- }
- }
-
- literal_dtor(&ZEND_OP2_LITERAL(init_opline));
- MAKE_NOP(init_opline);
- literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
- MAKE_NOP(send1_opline);
- if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &t);
- }
- break;
- } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("constant")-1 &&
- !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
- "constant", sizeof("constant")-1)) {
- zval t;
-
- if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) {
- literal_dtor(&ZEND_OP2_LITERAL(init_opline));
- MAKE_NOP(init_opline);
- literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
- MAKE_NOP(send1_opline);
- if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &t);
- }
- }
- break;
- /* dirname(IS_CONST/IS_STRING) -> IS_CONST/IS_STRING */
- } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("dirname")-1 &&
- !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
- "dirname", sizeof("dirname") - 1) &&
- IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
- zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0);
- ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname));
- if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) {
- zval t;
-
- ZVAL_STR(&t, dirname);
- literal_dtor(&ZEND_OP2_LITERAL(init_opline));
- MAKE_NOP(init_opline);
- literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
- MAKE_NOP(send1_opline);
- if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- opline->extended_value = 0;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &t);
- }
- } else {
- zend_string_release_ex(dirname, 0);
- }
- break;
- }
- }
- /* don't colllect constants after any other function call */
- collect_constants = 0;
- break;
- }
- case ZEND_STRLEN:
- if (opline->op1_type == IS_CONST) {
- zval t;
-
- if (zend_optimizer_eval_strlen(&t, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &t)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- zend_optimizer_update_op1_const(op_array, opline, &t);
- }
- }
- }
- break;
- case ZEND_DEFINED:
- {
- zval c;
- if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &c, 0)) {
- break;
- }
- ZVAL_TRUE(&c);
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
- MAKE_NOP(opline);
- } else {
- opline->opcode = ZEND_QM_ASSIGN;
- zend_optimizer_update_op1_const(op_array, opline, &c);
- }
- }
- break;
- case ZEND_DECLARE_CONST:
- if (collect_constants &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
- Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_STRING) {
- zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline));
- }
- break;
-#if 0
- /* see ext/opcache/tests/bug78961.phpt */
-// case ZEND_FETCH_R:
- case ZEND_FETCH_W:
-// case ZEND_FETCH_RW:
- case ZEND_FETCH_IS:
-// case ZEND_FETCH_FUNC_ARG:
- case ZEND_FETCH_UNSET:
- /* convert FETCH $GLOBALS (global), FETCH_DIM $x into FETCH $x (global) */
- if ((opline->extended_value & ZEND_FETCH_GLOBAL) != 0 &&
- opline->op1_type == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
- zend_string_equals_literal(Z_STR(ZEND_OP1_LITERAL(opline)), "GLOBALS") &&
- ((opline + 1)->opcode == opline->opcode + 1 ||
- ((opline + 1)->opcode == ZEND_UNSET_DIM &&
- opline->opcode == ZEND_FETCH_UNSET) ||
- ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ &&
- opline->opcode == ZEND_FETCH_IS)) &&
- (opline + 1)->op1_type == opline->result_type &&
- (opline + 1)->op1.var == opline->result.var &&
- ((opline + 1)->op2_type != IS_CONST ||
- Z_TYPE(ZEND_OP2_LITERAL(opline + 1)) < IS_ARRAY)) {
-
- if ((opline + 1)->opcode == ZEND_UNSET_DIM) {
- (opline + 1)->opcode = ZEND_UNSET_VAR;
- (opline + 1)->extended_value = ZEND_FETCH_GLOBAL;
- } else if ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) {
- (opline + 1)->opcode = ZEND_ISSET_ISEMPTY_VAR;
- (opline + 1)->extended_value |= ZEND_FETCH_GLOBAL;
- } else {
- (opline + 1)->opcode = opline->opcode;
- (opline + 1)->extended_value = ZEND_FETCH_GLOBAL;
- }
- (opline + 1)->op1_type = (opline + 1)->op2_type;
- (opline + 1)->op1 = (opline + 1)->op2;
- if ((opline + 1)->op1_type == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline + 1)) != IS_STRING) {
-
- convert_to_string(&ZEND_OP1_LITERAL(opline + 1));
- zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline + 1)));
- }
- SET_UNUSED((opline + 1)->op2);
- MAKE_NOP(opline);
- }
- break;
-#endif
-
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
- in case we know it wouldn't jump */
- if (opline->op1_type == IS_CONST) {
- if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
- if (opline->opcode == ZEND_JMPZ_EX) {
- opline->opcode = ZEND_QM_ASSIGN;
- zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
- ZVAL_TRUE(&ZEND_OP1_LITERAL(opline));
- opline->op2.num = 0;
- break;
- }
- } else {
- if (opline->opcode == ZEND_JMPNZ_EX) {
- opline->opcode = ZEND_QM_ASSIGN;
- zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
- ZVAL_FALSE(&ZEND_OP1_LITERAL(opline));
- opline->op2.num = 0;
- break;
- }
- }
- }
- collect_constants = 0;
- break;
-
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- if (opline->op1_type == IS_CONST) {
- int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
-
- if (opline->opcode == ZEND_JMPZ) {
- should_jmp = !should_jmp;
- }
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- opline->op1_type = IS_UNUSED;
- if (should_jmp) {
- opline->opcode = ZEND_JMP;
- COPY_NODE(opline->op1, opline->op2);
- opline->op2.num = 0;
- } else {
- MAKE_NOP(opline);
- break;
- }
- }
- collect_constants = 0;
- break;
-
- case ZEND_JMPZNZ:
- if (opline->op1_type == IS_CONST) {
- zend_op *target_opline;
-
- if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
- target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
- } else {
- target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
- }
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
- opline->op1_type = IS_UNUSED;
- opline->opcode = ZEND_JMP;
- }
- collect_constants = 0;
- break;
-
- case ZEND_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_THROW:
- case ZEND_MATCH_ERROR:
- case ZEND_CATCH:
- case ZEND_FAST_CALL:
- case ZEND_FAST_RET:
- case ZEND_JMP:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- collect_constants = 0;
- break;
- }
- opline++;
- }
-}
diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c
deleted file mode 100644
index f98c41848c..0000000000
--- a/ext/opcache/Optimizer/pass3.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-/* pass 3: (Jump optimization)
- * - optimize series of JMPs
- */
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-
-/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
-static zend_always_inline int in_hitlist(zend_op *target, zend_op **jmp_hitlist, int jmp_hitlist_count)
-{
- int i;
-
- for (i = 0; i < jmp_hitlist_count; i++) {
- if (jmp_hitlist[i] == target) {
- return 1;
- }
- }
- return 0;
-}
-
-#define CHECK_LOOP(target) \
- if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
- jmp_hitlist[jmp_hitlist_count++] = target; \
- } else { \
- break; \
- }
-
-void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_op *opline;
- zend_op *end;
- zend_op *target;
- zend_op **jmp_hitlist;
- int jmp_hitlist_count;
- ALLOCA_FLAG(use_heap);
-
- jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap);
- opline = op_array->opcodes;
- end = opline + op_array->last;
-
- while (opline < end) {
-
- switch (opline->opcode) {
- case ZEND_JMP:
- jmp_hitlist_count = 0;
-
- target = ZEND_OP1_JMP_ADDR(opline);
- while (1) {
- if (target->opcode == ZEND_JMP) {
- /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
- target = ZEND_OP1_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_NOP) {
- target = target + 1;
- } else {
- break;
- }
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
- }
-
- if (target == opline + 1) {
- /* convert L: JMP L+1 to NOP */
- MAKE_NOP(opline);
- } else if (target->opcode == ZEND_JMPZNZ) {
- /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
- *opline = *target;
- if (opline->op1_type == IS_CONST) {
- zval zv;
- ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
- opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
- }
- goto optimize_jmpznz;
- } else if ((target->opcode == ZEND_RETURN ||
- target->opcode == ZEND_RETURN_BY_REF ||
- target->opcode == ZEND_GENERATOR_RETURN ||
- target->opcode == ZEND_EXIT) &&
- !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
- /* JMP L, L: RETURN to immediate RETURN */
- *opline = *target;
- if (opline->op1_type == IS_CONST) {
- zval zv;
- ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
- opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
- }
- } else if (opline > op_array->opcodes &&
- ((opline-1)->opcode == ZEND_JMPZ ||
- (opline-1)->opcode == ZEND_JMPNZ)) {
- if (ZEND_OP2_JMP_ADDR(opline-1) == target) {
- /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
- if ((opline-1)->op1_type == IS_CV) {
- (opline-1)->opcode = ZEND_CHECK_VAR;
- (opline-1)->op2.num = 0;
- } else if ((opline-1)->op1_type & (IS_TMP_VAR|IS_VAR)) {
- (opline-1)->opcode = ZEND_FREE;
- (opline-1)->op2.num = 0;
- } else {
- MAKE_NOP(opline-1);
- }
- } else {
- /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
- if ((opline-1)->opcode == ZEND_JMPZ) {
- (opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), target);
- } else {
- (opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), ZEND_OP2_JMP_ADDR(opline-1));
- ZEND_SET_OP_JMP_ADDR((opline-1), (opline-1)->op2, target);
- }
- (opline-1)->opcode = ZEND_JMPZNZ;
- }
- }
- break;
-
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- jmp_hitlist_count = 0;
-
- target = ZEND_OP2_JMP_ADDR(opline);
- while (1) {
- if (target->opcode == ZEND_JMP) {
- target = ZEND_OP1_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_NOP) {
- target = target + 1;
- } else {
- break;
- }
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
- }
- break;
-
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- jmp_hitlist_count = 0;
-
- target = ZEND_OP2_JMP_ADDR(opline);
- while (1) {
- if (target->opcode == ZEND_JMP) {
- /* plain JMP */
- /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
- target = ZEND_OP1_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == opline->opcode &&
- SAME_VAR(opline->op1, target->op1)) {
- /* same opcode and same var as this opcode */
- /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
- target = ZEND_OP2_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == INV_COND(opline->opcode) &&
- SAME_VAR(opline->op1, target->op1)) {
- /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
- JMPZ(X,L1+1) */
- target = target + 1;
- } else if (target->opcode == ZEND_JMPZNZ &&
- SAME_VAR(opline->op1, target->op1)) {
- target = (opline->opcode == ZEND_JMPZ) ?
- ZEND_OP2_JMP_ADDR(target) :
- ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_NOP) {
- target = target + 1;
- } else {
- break;
- }
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
- }
-
- /* convert L: JMPZ L+1 to NOP */
- if (target == opline + 1) {
- if (opline->op1_type == IS_CV) {
- opline->opcode = ZEND_CHECK_VAR;
- opline->op2.num = 0;
- } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- opline->opcode = ZEND_FREE;
- opline->op2.num = 0;
- } else {
- MAKE_NOP(opline);
- }
- }
- break;
-
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- jmp_hitlist_count = 0;
-
- target = ZEND_OP2_JMP_ADDR(opline);
- while (1) {
- if (target->opcode == ZEND_JMP) {
- /* plain JMP */
- /* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */
- target = ZEND_OP1_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == opline->opcode-3 &&
- (SAME_VAR(target->op1, opline->result) ||
- SAME_VAR(target->op1, opline->op1))) {
- /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to
- JMPZ_EX(X,L2) */
- target = ZEND_OP2_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == opline->opcode &&
- target->result.var == opline->result.var &&
- (SAME_VAR(target->op1, opline->result) ||
- SAME_VAR(target->op1, opline->op1))) {
- /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to
- JMPZ_EX(X,L2) */
- target = ZEND_OP2_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_JMPZNZ &&
- (SAME_VAR(target->op1, opline->result) ||
- SAME_VAR(target->op1, opline->op1))) {
- /* Check for JMPZNZ with same cond variable */
- target = (opline->opcode == ZEND_JMPZ_EX) ?
- ZEND_OP2_JMP_ADDR(target) :
- ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
- CHECK_LOOP(target);
- } else if (target->opcode == INV_EX_COND(opline->opcode) &&
- (SAME_VAR(target->op1, opline->result) ||
- SAME_VAR(target->op1, opline->op1))) {
- /* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to
- JMPZ_EX(X,L1+1) */
- target = target + 1;
- } else if (target->opcode == INV_EX_COND_EX(opline->opcode) &&
- target->result.var == opline->result.var &&
- (SAME_VAR(target->op1, opline->result) ||
- SAME_VAR(target->op1, opline->op1))) {
- /* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to
- JMPZ_EX(X,L1+1) */
- target = target + 1;
- } else if (target->opcode == ZEND_BOOL &&
- (SAME_VAR(target->op1, opline->result) ||
- SAME_VAR(target->op1, opline->op1))) {
- /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
- Z = JMPZ_EX(X,L1+1) */
-
- /* NOTE: This optimization pattern is not safe, but works, */
- /* because result of JMPZ_EX instruction */
- /* is not used on the following path and */
- /* should be used once on the branch path. */
- /* */
- /* The pattern works well only if jums processed in */
- /* direct order, otherwise it breaks JMPZ_EX */
- /* sequences too early. */
- opline->result.var = target->result.var;
- target = target + 1;
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_NOP) {
- target = target + 1;
- } else {
- break;
- }
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
- }
-
- /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
- if (target == opline + 1) {
- opline->opcode = ZEND_BOOL;
- opline->op2.num = 0;
- }
- break;
-
- case ZEND_JMPZNZ:
-optimize_jmpznz:
- jmp_hitlist_count = 0;
- target = ZEND_OP2_JMP_ADDR(opline);
- while (1) {
- if (target->opcode == ZEND_JMP) {
- /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
- target = ZEND_OP1_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
- SAME_VAR(target->op1, opline->op1)) {
- /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
- target = ZEND_OP2_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_JMPNZ &&
- SAME_VAR(target->op1, opline->op1)) {
- /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
- target = target + 1;
- } else if (target->opcode == ZEND_NOP) {
- target = target + 1;
- } else {
- break;
- }
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
- }
-
- jmp_hitlist_count = 0;
- target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
- while (1) {
- if (target->opcode == ZEND_JMP) {
- /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
- target = ZEND_OP1_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_JMPNZ &&
- SAME_VAR(target->op1, opline->op1)) {
- /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
- target = ZEND_OP2_JMP_ADDR(target);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_JMPZ &&
- SAME_VAR(target->op1, opline->op1)) {
- /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
- target = target + 1;
- } else if (target->opcode == ZEND_JMPZNZ &&
- SAME_VAR(target->op1, opline->op1)) {
- /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
- target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
- CHECK_LOOP(target);
- } else if (target->opcode == ZEND_NOP) {
- target = target + 1;
- } else {
- break;
- }
- opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target);
- }
-
- if (ZEND_OP2_JMP_ADDR(opline) == target &&
- !(opline->op1_type & (IS_VAR|IS_TMP_VAR))) {
- /* JMPZNZ(?,L,L) -> JMP(L) */
- opline->opcode = ZEND_JMP;
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
- SET_UNUSED(opline->op1);
- SET_UNUSED(opline->op2);
- opline->extended_value = 0;
- }
- /* Don't convert JMPZNZ back to JMPZ/JMPNZ, because the
- following JMP is not removed yet. */
- break;
- }
- opline++;
- }
- free_alloca(jmp_hitlist, use_heap);
-}
diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c
deleted file mode 100644
index b1979b68a8..0000000000
--- a/ext/opcache/Optimizer/sccp.c
+++ /dev/null
@@ -1,2524 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, SCCP - Sparse Conditional Constant Propagation |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Nikita Popov <nikic@php.net> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "zend_type_info.h"
-#include "ZendAccelerator.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "Optimizer/zend_call_graph.h"
-#include "Optimizer/zend_inference.h"
-#include "Optimizer/scdf.h"
-#include "Optimizer/zend_dump.h"
-#include "ext/standard/php_string.h"
-#include "zend_exceptions.h"
-
-/* This implements sparse conditional constant propagation (SCCP) based on the SCDF framework. The
- * used value lattice is defined as follows:
- *
- * BOT < {constant values} < TOP
- *
- * TOP indicates an underdefined value, i.e. that we do not yet know the value of variable.
- * BOT indicates an overdefined value, i.e. that we know the variable to be non-constant.
- *
- * All variables are optimistically initialized to TOP, apart from the implicit variables defined
- * at the start of the first block. Note that variables that MAY_BE_REF are *not* initialized to
- * BOT. We rely on the fact that any operation resulting in a reference will produce a BOT anyway.
- * This is better because such operations might never be reached due to the conditional nature of
- * the algorithm.
- *
- * The meet operation for phi functions is defined as follows:
- * BOT + any = BOT
- * TOP + any = any
- * C_i + C_i = C_i (i.e. two equal constants)
- * C_i + C_j = BOT (i.e. two different constants)
- *
- * When evaluating instructions TOP and BOT are handled as follows:
- * a) If any operand is BOT, the result is BOT. The main exception to this is op1 of ASSIGN, which
- * is ignored. However, if the op1 MAY_BE_REF we do have to propagate the BOT.
- * b) Otherwise, if the instruction can never be evaluated (either in general, or with the
- * specific modifiers) the result is BOT.
- * c) Otherwise, if any operand is TOP, the result is TOP.
- * d) Otherwise (at this point all operands are known and constant), if we can compute the result
- * for these specific constants (without throwing notices or similar) then that is the result.
- * e) Otherwise the result is BOT.
- *
- * It is sometimes possible to determine a result even if one argument is TOP / BOT, e.g. for things
- * like BOT*0. Right now we don't bother with this -- the only thing that is done is evaluating
- * TYPE_CHECKS based on the type information.
- *
- * Feasible successors for conditional branches are determined as follows:
- * a) If we don't support the branch type or branch on BOT, all successors are feasible.
- * b) Otherwise, if we branch on TOP none of the successors are feasible.
- * c) Otherwise (we branch on a constant), the feasible successors are marked based on the constant
- * (usually only one successor will be feasible).
- *
- * The original SCCP algorithm is extended with ability to propagate constant array
- * elements and object properties. The extension is based on a variation of Array
- * SSA form and its application to Spare Constant Propagation, described at
- * "Array SSA Form" by Vivek Sarkar, Kathleen Knobe and Stephen Fink in chapter
- * 16 of the SSA book.
- */
-
-#define SCP_DEBUG 0
-
-typedef struct _sccp_ctx {
- scdf_ctx scdf;
- zend_call_info **call_map;
- zval *values;
- zval top;
- zval bot;
-} sccp_ctx;
-
-#define TOP ((zend_uchar)-1)
-#define BOT ((zend_uchar)-2)
-#define PARTIAL_ARRAY ((zend_uchar)-3)
-#define PARTIAL_OBJECT ((zend_uchar)-4)
-#define IS_TOP(zv) (Z_TYPE_P(zv) == TOP)
-#define IS_BOT(zv) (Z_TYPE_P(zv) == BOT)
-#define IS_PARTIAL_ARRAY(zv) (Z_TYPE_P(zv) == PARTIAL_ARRAY)
-#define IS_PARTIAL_OBJECT(zv) (Z_TYPE_P(zv) == PARTIAL_OBJECT)
-
-#define MAKE_PARTIAL_ARRAY(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_ARRAY | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
-#define MAKE_PARTIAL_OBJECT(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
-
-#define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP)
-#define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT)
-
-static void scp_dump_value(zval *zv) {
- if (IS_TOP(zv)) {
- fprintf(stderr, " top");
- } else if (IS_BOT(zv)) {
- fprintf(stderr, " bot");
- } else if (Z_TYPE_P(zv) == IS_ARRAY || IS_PARTIAL_ARRAY(zv)) {
- fprintf(stderr, " %s[", IS_PARTIAL_ARRAY(zv) ? "partial " : "");
- zend_dump_ht(Z_ARRVAL_P(zv));
- fprintf(stderr, "]");
- } else if (IS_PARTIAL_OBJECT(zv)) {
- fprintf(stderr, " {");
- zend_dump_ht(Z_ARRVAL_P(zv));
- fprintf(stderr, "}");
- } else {
- zend_dump_const(zv);
- }
-}
-
-static void empty_partial_array(zval *zv)
-{
- MAKE_PARTIAL_ARRAY(zv);
- Z_ARR_P(zv) = zend_new_array(8);
-}
-
-static void dup_partial_array(zval *dst, zval *src)
-{
- MAKE_PARTIAL_ARRAY(dst);
- Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
-}
-
-static void empty_partial_object(zval *zv)
-{
- MAKE_PARTIAL_OBJECT(zv);
- Z_ARR_P(zv) = zend_new_array(8);
-}
-
-static void dup_partial_object(zval *dst, zval *src)
-{
- MAKE_PARTIAL_OBJECT(dst);
- Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
-}
-
-static inline zend_bool value_known(zval *zv) {
- return !IS_TOP(zv) && !IS_BOT(zv);
-}
-
-/* Sets new value for variable and ensures that it is lower or equal
- * the previous one in the constant propagation lattice. */
-static void set_value(scdf_ctx *scdf, sccp_ctx *ctx, int var, zval *new) {
- zval *value = &ctx->values[var];
- if (IS_BOT(value) || IS_TOP(new)) {
- return;
- }
-
-#if SCP_DEBUG
- fprintf(stderr, "Lowering #%d.", var);
- zend_dump_var(scdf->op_array, IS_CV, scdf->ssa->vars[var].var);
- fprintf(stderr, " from");
- scp_dump_value(value);
- fprintf(stderr, " to");
- scp_dump_value(new);
- fprintf(stderr, "\n");
-#endif
-
- if (IS_TOP(value) || IS_BOT(new)) {
- zval_ptr_dtor_nogc(value);
- ZVAL_COPY(value, new);
- scdf_add_to_worklist(scdf, var);
- return;
- }
-
- /* Always replace PARTIAL_(ARRAY|OBJECT), as new maybe changed by join_partial_(arrays|object) */
- if (IS_PARTIAL_ARRAY(new) || IS_PARTIAL_OBJECT(new)) {
- if (Z_TYPE_P(value) != Z_TYPE_P(new)
- || zend_hash_num_elements(Z_ARR_P(new)) != zend_hash_num_elements(Z_ARR_P(value))) {
- zval_ptr_dtor_nogc(value);
- ZVAL_COPY(value, new);
- scdf_add_to_worklist(scdf, var);
- }
- return;
- }
-
-#if ZEND_DEBUG
- ZEND_ASSERT(zend_is_identical(value, new));
-#endif
-}
-
-static zval *get_op1_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
- if (opline->op1_type == IS_CONST) {
- return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op1.constant);
- } else if (ssa_op->op1_use != -1) {
- return &ctx->values[ssa_op->op1_use];
- } else {
- return NULL;
- }
-}
-
-static zval *get_op2_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
- if (opline->op2_type == IS_CONST) {
- return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op2.constant);
- } else if (ssa_op->op2_use != -1) {
- return &ctx->values[ssa_op->op2_use];
- } else {
- return NULL;
- }
-}
-
-static zend_bool can_replace_op1(
- const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
- switch (opline->opcode) {
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_REF:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_OBJ_W:
- case ZEND_FETCH_OBJ_RW:
- case ZEND_FETCH_OBJ_UNSET:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_FETCH_LIST_W:
- case ZEND_UNSET_DIM:
- case ZEND_UNSET_OBJ:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_UNPACK:
- case ZEND_SEND_ARRAY:
- case ZEND_SEND_USER:
- case ZEND_FE_RESET_RW:
- return 0;
- /* Do not accept CONST */
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- case ZEND_BIND_STATIC:
- case ZEND_BIND_GLOBAL:
- case ZEND_MAKE_REF:
- case ZEND_UNSET_CV:
- case ZEND_ISSET_ISEMPTY_CV:
- return 0;
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
- case ZEND_YIELD:
- return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
- case ZEND_VERIFY_RETURN_TYPE:
- // TODO: This would require a non-local change ???
- return 0;
- case ZEND_OP_DATA:
- return (opline - 1)->opcode != ZEND_ASSIGN_OBJ_REF &&
- (opline - 1)->opcode != ZEND_ASSIGN_STATIC_PROP_REF;
- default:
- if (ssa_op->op1_def != -1) {
- ZEND_UNREACHABLE();
- return 0;
- }
- }
-
- return 1;
-}
-
-static zend_bool can_replace_op2(
- const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
- switch (opline->opcode) {
- /* Do not accept CONST */
- case ZEND_DECLARE_CLASS_DELAYED:
- case ZEND_BIND_LEXICAL:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- return 0;
- }
- return 1;
-}
-
-static zend_bool try_replace_op1(
- sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
- if (ssa_op->op1_use == var && can_replace_op1(ctx->scdf.op_array, opline, ssa_op)) {
- zval zv;
- ZVAL_COPY(&zv, value);
- if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
- return 1;
- } else {
- // TODO: check the following special cases ???
- switch (opline->opcode) {
- case ZEND_CASE:
- opline->opcode = ZEND_IS_EQUAL;
- goto replace_op1_simple;
- case ZEND_CASE_STRICT:
- opline->opcode = ZEND_IS_IDENTICAL;
- goto replace_op1_simple;
- case ZEND_FETCH_LIST_R:
- case ZEND_SWITCH_STRING:
- case ZEND_SWITCH_LONG:
- case ZEND_MATCH:
-replace_op1_simple:
- if (Z_TYPE(zv) == IS_STRING) {
- zend_string_hash_val(Z_STR(zv));
- }
- opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
- opline->op1_type = IS_CONST;
- return 1;
- case ZEND_INSTANCEOF:
- zval_ptr_dtor_nogc(&zv);
- ZVAL_FALSE(&zv);
- opline->opcode = ZEND_QM_ASSIGN;
- opline->op1_type = IS_CONST;
- opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
- opline->op2_type = IS_UNUSED;
- if (ssa_op->op2_use >= 0) {
- ZEND_ASSERT(ssa_op->op2_def == -1);
- zend_ssa_unlink_use_chain(ctx->scdf.ssa, ssa_op - ctx->scdf.ssa->ops, ssa_op->op2_use);
- ssa_op->op2_use = -1;
- ssa_op->op2_use_chain = -1;
- }
- return 1;
- default:
- break;
- }
- zval_ptr_dtor_nogc(&zv);
- }
- }
- return 0;
-}
-
-static zend_bool try_replace_op2(
- sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
- if (ssa_op->op2_use == var && can_replace_op2(ctx->scdf.op_array, opline, ssa_op)) {
- zval zv;
- ZVAL_COPY(&zv, value);
- if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline, &zv)) {
- return 1;
- } else {
- switch (opline->opcode) {
- case ZEND_FETCH_CLASS:
- if (Z_TYPE(zv) == IS_STRING) {
- ZEND_ASSERT((opline + 1)->opcode == ZEND_INSTANCEOF);
- ZEND_ASSERT(ssa_op->result_def == (ssa_op + 1)->op2_use);
- if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline + 1, &zv)) {
- zend_ssa_op *next_op = ssa_op + 1;
- zend_ssa_unlink_use_chain(ctx->scdf.ssa, next_op - ctx->scdf.ssa->ops, next_op->op2_use);
- next_op->op2_use = -1;
- next_op->op2_use_chain = -1;
- zend_ssa_remove_result_def(ctx->scdf.ssa, ssa_op);
- MAKE_NOP(opline);
- return 1;
- }
- }
- default:
- break;
- }
- zval_ptr_dtor_nogc(&zv);
- }
- }
- return 0;
-}
-
-static inline int ct_eval_binary_op(zval *result, zend_uchar binop, zval *op1, zval *op2) {
- /* TODO: We could implement support for evaluation of + on partial arrays. */
- if (IS_PARTIAL_ARRAY(op1) || IS_PARTIAL_ARRAY(op2)) {
- return FAILURE;
- }
-
- return zend_optimizer_eval_binary_op(result, binop, op1, op2);
-}
-
-static inline int ct_eval_bool_cast(zval *result, zval *op) {
- if (IS_PARTIAL_ARRAY(op)) {
- if (zend_hash_num_elements(Z_ARRVAL_P(op)) == 0) {
- /* An empty partial array may be non-empty at runtime, we don't know whether the
- * result will be true or false. */
- return FAILURE;
- }
-
- ZVAL_TRUE(result);
- return SUCCESS;
- }
-
- ZVAL_BOOL(result, zend_is_true(op));
- return SUCCESS;
-}
-
-static inline int zval_to_string_offset(zend_long *result, zval *op) {
- switch (Z_TYPE_P(op)) {
- case IS_LONG:
- *result = Z_LVAL_P(op);
- return SUCCESS;
- case IS_STRING:
- if (IS_LONG == is_numeric_string(
- Z_STRVAL_P(op), Z_STRLEN_P(op), result, NULL, 0)) {
- return SUCCESS;
- }
- return FAILURE;
- default:
- return FAILURE;
- }
-}
-
-static inline int fetch_array_elem(zval **result, zval *op1, zval *op2) {
- switch (Z_TYPE_P(op2)) {
- case IS_NULL:
- *result = zend_hash_find(Z_ARR_P(op1), ZSTR_EMPTY_ALLOC());
- return SUCCESS;
- case IS_FALSE:
- *result = zend_hash_index_find(Z_ARR_P(op1), 0);
- return SUCCESS;
- case IS_TRUE:
- *result = zend_hash_index_find(Z_ARR_P(op1), 1);
- return SUCCESS;
- case IS_LONG:
- *result = zend_hash_index_find(Z_ARR_P(op1), Z_LVAL_P(op2));
- return SUCCESS;
- case IS_DOUBLE:
- *result = zend_hash_index_find(Z_ARR_P(op1), zend_dval_to_lval(Z_DVAL_P(op2)));
- return SUCCESS;
- case IS_STRING:
- *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
- return SUCCESS;
- default:
- return FAILURE;
- }
-}
-
-static inline int ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) {
- if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
- zval *value;
- if (fetch_array_elem(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
- ZVAL_COPY(result, value);
- return SUCCESS;
- }
- } else if (support_strings && Z_TYPE_P(op1) == IS_STRING) {
- zend_long index;
- if (zval_to_string_offset(&index, op2) == FAILURE) {
- return FAILURE;
- }
- if (index >= 0 && index < Z_STRLEN_P(op1)) {
- ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
- return SUCCESS;
- }
- }
- return FAILURE;
-}
-
-/* op1 may be NULL here to indicate an unset value */
-static inline int ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) {
- zval zv;
- if (!(extended_value & ZEND_ISEMPTY)) {
- ZVAL_BOOL(result, op1 && Z_TYPE_P(op1) != IS_NULL);
- return SUCCESS;
- } else if (!op1) {
- ZVAL_TRUE(result);
- return SUCCESS;
- } else if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
- ZVAL_BOOL(result, Z_TYPE(zv) == IS_FALSE);
- return SUCCESS;
- } else {
- return FAILURE;
- }
-}
-
-static inline int ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
- if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
- zval *value;
- if (fetch_array_elem(&value, op1, op2) == FAILURE) {
- return FAILURE;
- }
- if (IS_PARTIAL_ARRAY(op1) && (!value || IS_BOT(value))) {
- return FAILURE;
- }
- return ct_eval_isset_isempty(result, extended_value, value);
- } else if (Z_TYPE_P(op1) == IS_STRING) {
- // TODO
- return FAILURE;
- } else {
- ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
- return SUCCESS;
- }
-}
-
-static inline int ct_eval_del_array_elem(zval *result, zval *key) {
- ZEND_ASSERT(IS_PARTIAL_ARRAY(result));
-
- switch (Z_TYPE_P(key)) {
- case IS_NULL:
- zend_hash_del(Z_ARR_P(result), ZSTR_EMPTY_ALLOC());
- break;
- case IS_FALSE:
- zend_hash_index_del(Z_ARR_P(result), 0);
- break;
- case IS_TRUE:
- zend_hash_index_del(Z_ARR_P(result), 1);
- break;
- case IS_LONG:
- zend_hash_index_del(Z_ARR_P(result), Z_LVAL_P(key));
- break;
- case IS_DOUBLE:
- zend_hash_index_del(Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)));
- break;
- case IS_STRING:
- zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
- break;
- default:
- return FAILURE;
- }
-
- return SUCCESS;
-}
-
-static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) {
- if (!key) {
- SEPARATE_ARRAY(result);
- if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) {
- Z_TRY_ADDREF_P(value);
- return SUCCESS;
- }
- return FAILURE;
- }
-
- switch (Z_TYPE_P(key)) {
- case IS_NULL:
- SEPARATE_ARRAY(result);
- value = zend_hash_update(Z_ARR_P(result), ZSTR_EMPTY_ALLOC(), value);
- break;
- case IS_FALSE:
- SEPARATE_ARRAY(result);
- value = zend_hash_index_update(Z_ARR_P(result), 0, value);
- break;
- case IS_TRUE:
- SEPARATE_ARRAY(result);
- value = zend_hash_index_update(Z_ARR_P(result), 1, value);
- break;
- case IS_LONG:
- SEPARATE_ARRAY(result);
- value = zend_hash_index_update(Z_ARR_P(result), Z_LVAL_P(key), value);
- break;
- case IS_DOUBLE:
- SEPARATE_ARRAY(result);
- value = zend_hash_index_update(
- Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)), value);
- break;
- case IS_STRING:
- SEPARATE_ARRAY(result);
- value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
- break;
- default:
- return FAILURE;
- }
-
- Z_TRY_ADDREF_P(value);
- return SUCCESS;
-}
-
-static inline int ct_eval_add_array_unpack(zval *result, zval *array) {
- zend_string *key;
- zval *value;
- if (Z_TYPE_P(array) != IS_ARRAY) {
- return FAILURE;
- }
-
- SEPARATE_ARRAY(result);
- ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(array), key, value) {
- if (key) {
- return FAILURE;
- }
- value = zend_hash_next_index_insert(Z_ARR_P(result), value);
- if (!value) {
- return FAILURE;
- }
- Z_TRY_ADDREF_P(value);
- } ZEND_HASH_FOREACH_END();
- return SUCCESS;
-}
-
-static inline int ct_eval_assign_dim(zval *result, zval *value, zval *key) {
- switch (Z_TYPE_P(result)) {
- case IS_NULL:
- case IS_FALSE:
- array_init(result);
- /* break missing intentionally */
- case IS_ARRAY:
- case PARTIAL_ARRAY:
- return ct_eval_add_array_elem(result, value, key);
- case IS_STRING:
- // TODO Before enabling this case, make sure ARRAY_DIM result op is correct
-#if 0
- zend_long index;
- zend_string *new_str, *value_str;
- if (!key || Z_TYPE_P(value) == IS_ARRAY
- || zval_to_string_offset(&index, key) == FAILURE || index < 0) {
- return FAILURE;
- }
-
- if (index >= Z_STRLEN_P(result)) {
- new_str = zend_string_alloc(index + 1, 0);
- memcpy(ZSTR_VAL(new_str), Z_STRVAL_P(result), Z_STRLEN_P(result));
- memset(ZSTR_VAL(new_str) + Z_STRLEN_P(result), ' ', index - Z_STRLEN_P(result));
- ZSTR_VAL(new_str)[index + 1] = 0;
- } else {
- new_str = zend_string_init(Z_STRVAL_P(result), Z_STRLEN_P(result), 0);
- }
-
- value_str = zval_get_string(value);
- ZVAL_STR(result, new_str);
- Z_STRVAL_P(result)[index] = ZSTR_VAL(value_str)[0];
- zend_string_release_ex(value_str, 0);
-#endif
- return FAILURE;
- default:
- return FAILURE;
- }
-}
-
-static inline int fetch_obj_prop(zval **result, zval *op1, zval *op2) {
- switch (Z_TYPE_P(op2)) {
- case IS_STRING:
- *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
- return SUCCESS;
- default:
- return FAILURE;
- }
-}
-
-static inline int ct_eval_fetch_obj(zval *result, zval *op1, zval *op2) {
- if (IS_PARTIAL_OBJECT(op1)) {
- zval *value;
- if (fetch_obj_prop(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
- ZVAL_COPY(result, value);
- return SUCCESS;
- }
- }
- return FAILURE;
-}
-
-static inline int ct_eval_isset_obj(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
- if (IS_PARTIAL_OBJECT(op1)) {
- zval *value;
- if (fetch_obj_prop(&value, op1, op2) == FAILURE) {
- return FAILURE;
- }
- if (!value || IS_BOT(value)) {
- return FAILURE;
- }
- return ct_eval_isset_isempty(result, extended_value, value);
- } else {
- ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
- return SUCCESS;
- }
-}
-
-static inline int ct_eval_del_obj_prop(zval *result, zval *key) {
- ZEND_ASSERT(IS_PARTIAL_OBJECT(result));
-
- switch (Z_TYPE_P(key)) {
- case IS_STRING:
- zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
- break;
- default:
- return FAILURE;
- }
-
- return SUCCESS;
-}
-
-static inline int ct_eval_add_obj_prop(zval *result, zval *value, zval *key) {
- switch (Z_TYPE_P(key)) {
- case IS_STRING:
- value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
- break;
- default:
- return FAILURE;
- }
-
- Z_TRY_ADDREF_P(value);
- return SUCCESS;
-}
-
-static inline int ct_eval_assign_obj(zval *result, zval *value, zval *key) {
- switch (Z_TYPE_P(result)) {
- case IS_NULL:
- case IS_FALSE:
- empty_partial_object(result);
- /* break missing intentionally */
- case PARTIAL_OBJECT:
- return ct_eval_add_obj_prop(result, value, key);
- default:
- return FAILURE;
- }
-}
-
-static inline int ct_eval_incdec(zval *result, zend_uchar opcode, zval *op1) {
- ZVAL_COPY(result, op1);
- if (opcode == ZEND_PRE_INC
- || opcode == ZEND_POST_INC
- || opcode == ZEND_PRE_INC_OBJ
- || opcode == ZEND_POST_INC_OBJ) {
- increment_function(result);
- } else {
- decrement_function(result);
- }
- return SUCCESS;
-}
-
-static inline void ct_eval_type_check(zval *result, uint32_t type_mask, zval *op1) {
- uint32_t type = Z_TYPE_P(op1);
- if (type == PARTIAL_ARRAY) {
- type = IS_ARRAY;
- } else if (type == PARTIAL_OBJECT) {
- type = IS_OBJECT;
- }
- ZVAL_BOOL(result, (type_mask >> type) & 1);
-}
-
-static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
- HashTable *ht;
- zend_bool res;
-
- if (Z_TYPE_P(op2) != IS_ARRAY) {
- return FAILURE;
- }
- ht = Z_ARRVAL_P(op2);
- if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
- res = zend_hash_exists(ht, Z_STR_P(op1));
- } else if (extended_value) {
- if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
- res = zend_hash_index_exists(ht, Z_LVAL_P(op1));
- } else {
- res = 0;
- }
- } else if (Z_TYPE_P(op1) <= IS_FALSE) {
- res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC());
- } else {
- zend_string *key;
- zval key_tmp;
-
- res = 0;
- ZEND_HASH_FOREACH_STR_KEY(ht, key) {
- ZVAL_STR(&key_tmp, key);
- if (zend_compare(op1, &key_tmp) == 0) {
- res = 1;
- break;
- }
- } ZEND_HASH_FOREACH_END();
- }
- ZVAL_BOOL(result, res);
- return SUCCESS;
-}
-
-static inline int ct_eval_array_key_exists(zval *result, zval *op1, zval *op2) {
- zval *value;
-
- if (Z_TYPE_P(op2) != IS_ARRAY && !IS_PARTIAL_ARRAY(op2)) {
- return FAILURE;
- }
- if (Z_TYPE_P(op1) != IS_STRING && Z_TYPE_P(op1) != IS_LONG && Z_TYPE_P(op1) != IS_NULL) {
- return FAILURE;
- }
- if (fetch_array_elem(&value, op2, op1) == FAILURE) {
- return FAILURE;
- }
- if (IS_PARTIAL_ARRAY(op2) && (!value || IS_BOT(value))) {
- return FAILURE;
- }
-
- ZVAL_BOOL(result, value != NULL);
- return SUCCESS;
-}
-
-static zend_bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **args) {
- /* Functions that can be evaluated independently of what the arguments are.
- * It's okay if these functions throw on invalid arguments, but they should not warn. */
- if (false
- || zend_string_equals_literal(name, "array_diff")
- || zend_string_equals_literal(name, "array_diff_assoc")
- || zend_string_equals_literal(name, "array_diff_key")
- || zend_string_equals_literal(name, "array_key_exists")
- || zend_string_equals_literal(name, "array_keys")
- || zend_string_equals_literal(name, "array_merge")
- || zend_string_equals_literal(name, "array_merge_recursive")
- || zend_string_equals_literal(name, "array_replace")
- || zend_string_equals_literal(name, "array_replace_recursive")
- || zend_string_equals_literal(name, "array_values")
- || zend_string_equals_literal(name, "base64_decode")
- || zend_string_equals_literal(name, "base64_encode")
- || zend_string_equals_literal(name, "imagetypes")
- || zend_string_equals_literal(name, "in_array")
- || zend_string_equals_literal(name, "ltrim")
- || zend_string_equals_literal(name, "php_sapi_name")
- || zend_string_equals_literal(name, "php_uname")
- || zend_string_equals_literal(name, "phpversion")
- || zend_string_equals_literal(name, "pow")
- || zend_string_equals_literal(name, "preg_quote")
- || zend_string_equals_literal(name, "rawurldecode")
- || zend_string_equals_literal(name, "rawurlencode")
- || zend_string_equals_literal(name, "rtrim")
- || zend_string_equals_literal(name, "serialize")
- || zend_string_equals_literal(name, "str_contains")
- || zend_string_equals_literal(name, "str_ends_with")
- || zend_string_equals_literal(name, "str_split")
- || zend_string_equals_literal(name, "str_starts_with")
- || zend_string_equals_literal(name, "strpos")
- || zend_string_equals_literal(name, "substr")
- || zend_string_equals_literal(name, "trim")
- || zend_string_equals_literal(name, "urldecode")
- || zend_string_equals_literal(name, "urlencode")
- || zend_string_equals_literal(name, "version_compare")
- ) {
- return true;
- }
-
- /* For the following functions we need to check arguments to prevent warnings during
- * evaluation. */
- if (num_args == 1) {
- if (zend_string_equals_literal(name, "array_flip")) {
- zval *entry;
-
- if (Z_TYPE_P(args[0]) != IS_ARRAY) {
- return false;
- }
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
- /* Throws warning for non int/string values. */
- if (Z_TYPE_P(entry) != IS_LONG && Z_TYPE_P(entry) != IS_STRING) {
- return false;
- }
- } ZEND_HASH_FOREACH_END();
- return true;
- }
- if (zend_string_equals_literal(name, "implode")) {
- zval *entry;
-
- if (Z_TYPE_P(args[0]) != IS_ARRAY) {
- return false;
- }
-
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
- /* May throw warning during conversion to string. */
- if (Z_TYPE_P(entry) > IS_STRING) {
- return false;
- }
- } ZEND_HASH_FOREACH_END();
- return true;
- }
- return false;
- }
-
- if (num_args == 2) {
- if (zend_string_equals_literal(name, "str_repeat")) {
- /* Avoid creating overly large strings at compile-time. */
- bool overflow;
- return Z_TYPE_P(args[0]) == IS_STRING
- && Z_TYPE_P(args[1]) == IS_LONG
- && zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) < 64 * 1024
- && !overflow;
- } else if (zend_string_equals_literal(name, "implode")) {
- zval *entry;
-
- if ((Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_ARRAY)
- && (Z_TYPE_P(args[0]) != IS_ARRAY || Z_TYPE_P(args[1]) != IS_STRING)) {
- return false;
- }
-
- /* May throw warning during conversion to string. */
- if (Z_TYPE_P(args[0]) == IS_ARRAY) {
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
- if (Z_TYPE_P(entry) > IS_STRING) {
- return false;
- }
- } ZEND_HASH_FOREACH_END();
- } else {
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[1]), entry) {
- if (Z_TYPE_P(entry) > IS_STRING) {
- return false;
- }
- } ZEND_HASH_FOREACH_END();
- }
- return true;
- }
- return false;
- }
-
- return false;
-}
-
-/* The functions chosen here are simple to implement and either likely to affect a branch,
- * or just happened to be commonly used with constant operands in WP (need to test other
- * applications as well, of course). */
-static inline int ct_eval_func_call(
- zend_op_array *op_array, zval *result, zend_string *name, uint32_t num_args, zval **args) {
- uint32_t i;
- zend_function *func = zend_hash_find_ptr(CG(function_table), name);
- if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
- return FAILURE;
- }
-
- if (num_args == 1) {
- /* Handle a few functions for which we manually implement evaluation here. */
- if (zend_string_equals_literal(name, "chr")) {
- zend_long c;
- if (Z_TYPE_P(args[0]) != IS_LONG) {
- return FAILURE;
- }
-
- c = Z_LVAL_P(args[0]) & 0xff;
- ZVAL_CHAR(result, c);
- return SUCCESS;
- } else if (zend_string_equals_literal(name, "count")) {
- if (Z_TYPE_P(args[0]) != IS_ARRAY) {
- return FAILURE;
- }
-
- ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0])));
- return SUCCESS;
- } else if (zend_string_equals_literal(name, "ini_get")) {
- zend_ini_entry *ini_entry;
-
- if (Z_TYPE_P(args[0]) != IS_STRING) {
- return FAILURE;
- }
-
- ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0]));
- if (!ini_entry) {
- ZVAL_FALSE(result);
- } else if (ini_entry->modifiable != ZEND_INI_SYSTEM) {
- return FAILURE;
- } else if (ini_entry->value) {
- ZVAL_STR_COPY(result, ini_entry->value);
- } else {
- ZVAL_EMPTY_STRING(result);
- }
- return SUCCESS;
- }
- }
-
- if (!can_ct_eval_func_call(name, num_args, args)) {
- return FAILURE;
- }
-
- zend_execute_data *prev_execute_data = EG(current_execute_data);
- zend_execute_data *execute_data, dummy_frame;
- zend_op dummy_opline;
-
- /* Add a dummy frame to get the correct strict_types behavior. */
- memset(&dummy_frame, 0, sizeof(zend_execute_data));
- memset(&dummy_opline, 0, sizeof(zend_op));
- dummy_frame.func = (zend_function *) op_array;
- dummy_frame.opline = &dummy_opline;
- dummy_opline.opcode = ZEND_DO_FCALL;
-
- execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval));
- memset(execute_data, 0, sizeof(zend_execute_data));
- execute_data->prev_execute_data = &dummy_frame;
- EG(current_execute_data) = execute_data;
-
- EX(func) = func;
- EX_NUM_ARGS() = num_args;
- for (i = 0; i < num_args; i++) {
- ZVAL_COPY(EX_VAR_NUM(i), args[i]);
- }
- ZVAL_NULL(result);
- func->internal_function.handler(execute_data, result);
- for (i = 0; i < num_args; i++) {
- zval_ptr_dtor_nogc(EX_VAR_NUM(i));
- }
-
- int retval = SUCCESS;
- if (EG(exception)) {
- zval_ptr_dtor(result);
- zend_clear_exception();
- retval = FAILURE;
- }
-
- efree(execute_data);
- EG(current_execute_data) = prev_execute_data;
- return retval;
-}
-
-#define SET_RESULT(op, zv) do { \
- if (ssa_op->op##_def >= 0) { \
- set_value(scdf, ctx, ssa_op->op##_def, zv); \
- } \
-} while (0)
-#define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot)
-#define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top)
-
-#define SKIP_IF_TOP(op) if (IS_TOP(op)) return;
-
-static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
- sccp_ctx *ctx = (sccp_ctx *) scdf;
- zval *op1, *op2, zv; /* zv is a temporary to hold result values */
-
- op1 = get_op1_value(ctx, opline, ssa_op);
- op2 = get_op2_value(ctx, opline, ssa_op);
-
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- /* The value of op1 is irrelevant here, because we are overwriting it
- * -- unless it can be a reference, in which case we propagate a BOT. */
- if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) {
- SET_RESULT_BOT(op1);
- } else {
- SET_RESULT(op1, op2);
- }
-
- SET_RESULT(result, op2);
- return;
- case ZEND_TYPE_CHECK:
- /* We may be able to evaluate TYPE_CHECK based on type inference info,
- * even if we don't know the precise value. */
- if (!value_known(op1)) {
- uint32_t type = ctx->scdf.ssa->var_info[ssa_op->op1_use].type;
- uint32_t expected_type_mask = opline->extended_value;
- if (!(type & expected_type_mask) && !(type & MAY_BE_UNDEF)) {
- ZVAL_FALSE(&zv);
- SET_RESULT(result, &zv);
- return;
- } else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type_mask))
- && !(expected_type_mask & MAY_BE_RESOURCE)) {
- ZVAL_TRUE(&zv);
- SET_RESULT(result, &zv);
- return;
- }
- }
- break;
- case ZEND_ASSIGN_DIM:
- {
- zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
-
- /* If $a in $a[$b]=$c is UNDEF, treat it like NULL. There is no warning. */
- if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) {
- op1 = &EG(uninitialized_zval);
- }
-
- if (IS_BOT(op1)) {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- return;
- }
-
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(data);
- if (op2) {
- SKIP_IF_TOP(op2);
- }
-
- if (op2 && IS_BOT(op2)) {
- /* Update of unknown index */
- SET_RESULT_BOT(result);
- if (ssa_op->op1_def >= 0) {
- empty_partial_array(&zv);
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&zv);
- } else {
- SET_RESULT_BOT(op1);
- }
- return;
- }
-
- if (IS_BOT(data)) {
-
- SET_RESULT_BOT(result);
- if ((IS_PARTIAL_ARRAY(op1)
- || Z_TYPE_P(op1) == IS_NULL
- || Z_TYPE_P(op1) == IS_FALSE
- || Z_TYPE_P(op1) == IS_ARRAY)
- && ssa_op->op1_def >= 0) {
-
- if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
- empty_partial_array(&zv);
- } else {
- dup_partial_array(&zv, op1);
- }
-
- if (!op2) {
- /* We can't add NEXT element into partial array (skip it) */
- SET_RESULT(op1, &zv);
- } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
- SET_RESULT(op1, &zv);
- } else {
- SET_RESULT_BOT(op1);
- }
-
- zval_ptr_dtor_nogc(&zv);
- } else {
- SET_RESULT_BOT(op1);
- }
-
- } else {
-
- if (IS_PARTIAL_ARRAY(op1)) {
- dup_partial_array(&zv, op1);
- } else {
- ZVAL_COPY(&zv, op1);
- }
-
- if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
- /* We can't add NEXT element into partial array (skip it) */
- SET_RESULT(result, data);
- SET_RESULT(op1, &zv);
- } else if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) {
- /* Mark array containing partial array as partial */
- if (IS_PARTIAL_ARRAY(data)) {
- MAKE_PARTIAL_ARRAY(&zv);
- }
- SET_RESULT(result, data);
- SET_RESULT(op1, &zv);
- } else {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- }
-
- zval_ptr_dtor_nogc(&zv);
- }
- return;
- }
-
- case ZEND_ASSIGN_OBJ:
- if (ssa_op->op1_def >= 0
- && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
- zend_ssa_var_info *var_info = &ctx->scdf.ssa->var_info[ssa_op->op1_use];
-
- /* Don't try to propagate assignments to (potentially) typed properties. We would
- * need to deal with errors and type conversions first. */
- if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- return;
- }
-
- if (IS_BOT(op1)) {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- return;
- }
-
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(data);
- SKIP_IF_TOP(op2);
-
- if (IS_BOT(op2)) {
- /* Update of unknown property */
- SET_RESULT_BOT(result);
- empty_partial_object(&zv);
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&zv);
- return;
- }
-
- if (IS_BOT(data)) {
- SET_RESULT_BOT(result);
- if (IS_PARTIAL_OBJECT(op1)
- || Z_TYPE_P(op1) == IS_NULL
- || Z_TYPE_P(op1) == IS_FALSE) {
-
- if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
- empty_partial_object(&zv);
- } else {
- dup_partial_object(&zv, op1);
- }
-
- if (ct_eval_del_obj_prop(&zv, op2) == SUCCESS) {
- SET_RESULT(op1, &zv);
- } else {
- SET_RESULT_BOT(op1);
- }
- zval_ptr_dtor_nogc(&zv);
- } else {
- SET_RESULT_BOT(op1);
- }
-
- } else {
-
- if (IS_PARTIAL_OBJECT(op1)) {
- dup_partial_object(&zv, op1);
- } else {
- ZVAL_COPY(&zv, op1);
- }
-
- if (ct_eval_assign_obj(&zv, data, op2) == SUCCESS) {
- SET_RESULT(result, data);
- SET_RESULT(op1, &zv);
- } else {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- }
-
- zval_ptr_dtor_nogc(&zv);
- }
- } else {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- }
- return;
-
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- {
- /* If the value of a SEND for an ICALL changes, we need to reconsider the
- * ICALL result value. Otherwise we can ignore the opcode. */
- zend_call_info *call;
- if (!ctx->call_map) {
- return;
- }
-
- call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
- if (IS_TOP(op1) || !call || !call->caller_call_opline
- || call->caller_call_opline->opcode != ZEND_DO_ICALL) {
- return;
- }
-
- opline = call->caller_call_opline;
- ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes];
- break;
- }
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- {
- zval *result = NULL;
-
- if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
- result = &ctx->values[ssa_op->result_use];
- if (IS_BOT(result)) {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- return;
- }
- SKIP_IF_TOP(result);
- }
-
- if (op1) {
- SKIP_IF_TOP(op1);
- }
-
- if (op2) {
- SKIP_IF_TOP(op2);
- }
-
- /* We want to avoid keeping around intermediate arrays for each SSA variable in the
- * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode
- * and use a NULL value everywhere else. */
- if (result && Z_TYPE_P(result) == IS_NULL) {
- SET_RESULT_BOT(result);
- return;
- }
-
- if (op2 && IS_BOT(op2)) {
- /* Update of unknown index */
- SET_RESULT_BOT(op1);
- if (ssa_op->result_def >= 0) {
- empty_partial_array(&zv);
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- } else {
- SET_RESULT_BOT(result);
- }
- return;
- }
-
- if ((op1 && IS_BOT(op1))
- || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
-
- SET_RESULT_BOT(op1);
- if (ssa_op->result_def >= 0) {
- if (!result) {
- empty_partial_array(&zv);
- } else {
- MAKE_PARTIAL_ARRAY(result);
- ZVAL_COPY_VALUE(&zv, result);
- ZVAL_NULL(result);
- }
- if (!op2) {
- /* We can't add NEXT element into partial array (skip it) */
- SET_RESULT(result, &zv);
- } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- } else {
- SET_RESULT_BOT(result);
- }
- zval_ptr_dtor_nogc(&zv);
- } else {
- /* If any operand is BOT, mark the result as BOT right away.
- * Exceptions to this rule are handled above. */
- SET_RESULT_BOT(result);
- }
-
- } else {
- if (result) {
- ZVAL_COPY_VALUE(&zv, result);
- ZVAL_NULL(result);
- } else {
- array_init(&zv);
- }
-
- if (op1) {
- if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
- /* We can't add NEXT element into partial array (skip it) */
- SET_RESULT(result, &zv);
- } else if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) {
- if (IS_PARTIAL_ARRAY(op1)) {
- MAKE_PARTIAL_ARRAY(&zv);
- }
- SET_RESULT(result, &zv);
- } else {
- SET_RESULT_BOT(result);
- }
- } else {
- SET_RESULT(result, &zv);
- }
-
- zval_ptr_dtor_nogc(&zv);
- }
- return;
- }
- case ZEND_ADD_ARRAY_UNPACK: {
- zval *result = &ctx->values[ssa_op->result_use];
- if (IS_BOT(result) || IS_BOT(op1)) {
- SET_RESULT_BOT(result);
- return;
- }
- SKIP_IF_TOP(result);
- SKIP_IF_TOP(op1);
-
- /* See comment for ADD_ARRAY_ELEMENT. */
- if (Z_TYPE_P(result) == IS_NULL) {
- SET_RESULT_BOT(result);
- return;
- }
- ZVAL_COPY_VALUE(&zv, result);
- ZVAL_NULL(result);
-
- if (ct_eval_add_array_unpack(&zv, op1) == SUCCESS) {
- SET_RESULT(result, &zv);
- } else {
- SET_RESULT_BOT(result);
- }
- zval_ptr_dtor_nogc(&zv);
- return;
- }
- case ZEND_NEW:
- if (ssa_op->result_def >= 0
- && ctx->scdf.ssa->vars[ssa_op->result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- empty_partial_object(&zv);
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- } else {
- SET_RESULT_BOT(result);
- }
- return;
- case ZEND_ASSIGN_STATIC_PROP_REF:
- case ZEND_ASSIGN_OBJ_REF:
- /* Handled here because we also need to BOT the OP_DATA operand, while the generic
- * code below will not do so. */
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- SET_RESULT_BOT(op2);
- opline++;
- ssa_op++;
- SET_RESULT_BOT(op1);
- break;
- }
-
- if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) {
- /* If any operand is BOT, mark the result as BOT right away.
- * Exceptions to this rule are handled above. */
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- SET_RESULT_BOT(op2);
- return;
- }
-
- switch (opline->opcode) {
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- case ZEND_DIV:
- case ZEND_MOD:
- case ZEND_POW:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_BOOL_XOR:
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
-
- if (ct_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- if (op1) {
- SKIP_IF_TOP(op1);
- }
- if (op2) {
- SKIP_IF_TOP(op2);
- }
- if (opline->opcode == ZEND_ASSIGN_OP) {
- if (ct_eval_binary_op(&zv, opline->extended_value, op1, op2) == SUCCESS) {
- SET_RESULT(op1, &zv);
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
- if ((IS_PARTIAL_ARRAY(op1) || Z_TYPE_P(op1) == IS_ARRAY)
- && ssa_op->op1_def >= 0 && op2) {
- zval tmp;
- zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
-
- SKIP_IF_TOP(data);
-
- if (ct_eval_fetch_dim(&tmp, op1, op2, 0) == SUCCESS) {
- if (IS_BOT(data)) {
- dup_partial_array(&zv, op1);
- ct_eval_del_array_elem(&zv, op2);
- SET_RESULT_BOT(result);
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&tmp);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
-
- if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- zval_ptr_dtor_nogc(&tmp);
- break;
- }
-
- if (IS_PARTIAL_ARRAY(op1)) {
- dup_partial_array(&zv, op1);
- } else {
- ZVAL_COPY(&zv, op1);
- }
-
- if (ct_eval_assign_dim(&zv, &tmp, op2) == SUCCESS) {
- SET_RESULT(result, &tmp);
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&tmp);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
-
- zval_ptr_dtor_nogc(&tmp);
- zval_ptr_dtor_nogc(&zv);
- }
- }
- } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
- if (op1 && IS_PARTIAL_OBJECT(op1)
- && ssa_op->op1_def >= 0
- && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- zval tmp;
- zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
-
- SKIP_IF_TOP(data);
-
- if (ct_eval_fetch_obj(&tmp, op1, op2) == SUCCESS) {
- if (IS_BOT(data)) {
- dup_partial_object(&zv, op1);
- ct_eval_del_obj_prop(&zv, op2);
- SET_RESULT_BOT(result);
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&tmp);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
-
- if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) {
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- zval_ptr_dtor_nogc(&tmp);
- break;
- }
-
- dup_partial_object(&zv, op1);
-
- if (ct_eval_assign_obj(&zv, &tmp, op2) == SUCCESS) {
- SET_RESULT(result, &tmp);
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&tmp);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
-
- zval_ptr_dtor_nogc(&tmp);
- zval_ptr_dtor_nogc(&zv);
- }
- }
- }
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- break;
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- if (op1) {
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
- if (IS_PARTIAL_OBJECT(op1)
- && ssa_op->op1_def >= 0
- && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- zval tmp1, tmp2;
-
- if (ct_eval_fetch_obj(&tmp1, op1, op2) == SUCCESS
- && ct_eval_incdec(&tmp2, opline->opcode, &tmp1) == SUCCESS) {
-
- dup_partial_object(&zv, op1);
- ct_eval_assign_obj(&zv, &tmp2, op2);
- if (opline->opcode == ZEND_PRE_INC_OBJ
- || opline->opcode == ZEND_PRE_DEC_OBJ) {
- SET_RESULT(result, &tmp2);
- } else {
- SET_RESULT(result, &tmp1);
- }
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- }
- }
- SET_RESULT_BOT(op1);
- SET_RESULT_BOT(result);
- break;
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- SKIP_IF_TOP(op1);
- if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
- SET_RESULT(op1, &zv);
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(op1);
- SET_RESULT_BOT(result);
- break;
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- SKIP_IF_TOP(op1);
- SET_RESULT(result, op1);
- if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
- SET_RESULT(op1, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(op1);
- break;
- case ZEND_BW_NOT:
- case ZEND_BOOL_NOT:
- SKIP_IF_TOP(op1);
- if (IS_PARTIAL_ARRAY(op1)) {
- SET_RESULT_BOT(result);
- break;
- }
- if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_CAST:
- SKIP_IF_TOP(op1);
- if (IS_PARTIAL_ARRAY(op1)) {
- SET_RESULT_BOT(result);
- break;
- }
- if (zend_optimizer_eval_cast(&zv, opline->extended_value, op1) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_BOOL:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- SKIP_IF_TOP(op1);
- if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_STRLEN:
- SKIP_IF_TOP(op1);
- if (zend_optimizer_eval_strlen(&zv, op1) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_YIELD_FROM:
- // tmp = yield from [] -> tmp = null
- SKIP_IF_TOP(op1);
- if (Z_TYPE_P(op1) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(op1)) == 0) {
- ZVAL_NULL(&zv);
- SET_RESULT(result, &zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_COUNT:
- SKIP_IF_TOP(op1);
- if (Z_TYPE_P(op1) == IS_ARRAY) {
- ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1)));
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_IN_ARRAY:
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
- if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_ARRAY_KEY_EXISTS:
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
- if (ct_eval_array_key_exists(&zv, op1, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_LIST_R:
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
-
- if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R)) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
-
- if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_OBJ_IS:
- if (op1) {
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
-
- if (ct_eval_fetch_obj(&zv, op1, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- if (op1) {
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
-
- if (ct_eval_isset_obj(&zv, opline->extended_value, op1, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_QM_ASSIGN:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_COPY_TMP:
- SET_RESULT(result, op1);
- break;
- case ZEND_JMP_NULL:
- switch (opline->extended_value) {
- case ZEND_SHORT_CIRCUITING_CHAIN_EXPR:
- ZVAL_NULL(&zv);
- break;
- case ZEND_SHORT_CIRCUITING_CHAIN_ISSET:
- ZVAL_FALSE(&zv);
- break;
- case ZEND_SHORT_CIRCUITING_CHAIN_EMPTY:
- ZVAL_TRUE(&zv);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- SET_RESULT(result, &zv);
- break;
-#if 0
- case ZEND_FETCH_CLASS:
- if (!op1) {
- SET_RESULT_BOT(result);
- break;
- }
- SET_RESULT(result, op1);
- break;
-#endif
- case ZEND_ISSET_ISEMPTY_CV:
- SKIP_IF_TOP(op1);
- if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_TYPE_CHECK:
- SKIP_IF_TOP(op1);
- ct_eval_type_check(&zv, opline->extended_value, op1);
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- case ZEND_INSTANCEOF:
- SKIP_IF_TOP(op1);
- ZVAL_FALSE(&zv);
- SET_RESULT(result, &zv);
- break;
- case ZEND_ROPE_INIT:
- SKIP_IF_TOP(op2);
- if (IS_PARTIAL_ARRAY(op2)) {
- SET_RESULT_BOT(result);
- break;
- }
- if (zend_optimizer_eval_cast(&zv, IS_STRING, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- // TODO The way this is currently implemented will result in quadratic runtime
- // This is not necessary, the way the algorithm works it's okay to reuse the same
- // string for all SSA vars with some extra checks
- SKIP_IF_TOP(op1);
- SKIP_IF_TOP(op2);
- if (ct_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
- SET_RESULT_BOT(result);
- break;
- case ZEND_DO_ICALL:
- {
- zend_call_info *call;
- zval *name, *args[3] = {NULL};
- int i;
-
- if (!ctx->call_map) {
- SET_RESULT_BOT(result);
- break;
- }
-
- call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
- name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
-
- /* We already know it can't be evaluated, don't bother checking again */
- if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
- break;
- }
-
- /* We're only interested in functions with up to three arguments right now */
- if (call->num_args > 3 || call->send_unpack) {
- SET_RESULT_BOT(result);
- break;
- }
-
- for (i = 0; i < call->num_args; i++) {
- zend_op *opline = call->arg_info[i].opline;
- if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
- SET_RESULT_BOT(result);
- return;
- }
-
- args[i] = get_op1_value(ctx, opline,
- &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
- if (args[i]) {
- if (IS_BOT(args[i]) || IS_PARTIAL_ARRAY(args[i])) {
- SET_RESULT_BOT(result);
- return;
- } else if (IS_TOP(args[i])) {
- return;
- }
- }
- }
-
- /* We didn't get a BOT argument, so value stays the same */
- if (!IS_TOP(&ctx->values[ssa_op->result_def])) {
- break;
- }
-
- if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
- SET_RESULT(result, &zv);
- zval_ptr_dtor_nogc(&zv);
- break;
- }
-
-#if 0
- /* sort out | uniq -c | sort -n */
- fprintf(stderr, "%s\n", Z_STRVAL_P(name));
- /*if (args[1]) {
- php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
- } else {
- php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
- }*/
-#endif
-
- SET_RESULT_BOT(result);
- break;
- }
- default:
- {
- /* If we have no explicit implementation return BOT */
- SET_RESULT_BOT(result);
- SET_RESULT_BOT(op1);
- SET_RESULT_BOT(op2);
- break;
- }
- }
-}
-
-/* Returns whether there is a successor */
-static void sccp_mark_feasible_successors(
- scdf_ctx *scdf,
- int block_num, zend_basic_block *block,
- zend_op *opline, zend_ssa_op *ssa_op) {
- sccp_ctx *ctx = (sccp_ctx *) scdf;
- zval *op1, zv;
- int s;
-
- /* We can't determine the branch target at compile-time for these */
- switch (opline->opcode) {
- case ZEND_ASSERT_CHECK:
- case ZEND_CATCH:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
- scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
- return;
- }
-
- op1 = get_op1_value(ctx, opline, ssa_op);
-
- /* Branch target can be either one */
- if (!op1 || IS_BOT(op1)) {
- for (s = 0; s < block->successors_count; s++) {
- scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
- }
- return;
- }
-
- /* Branch target not yet known */
- if (IS_TOP(op1)) {
- return;
- }
-
- switch (opline->opcode) {
- case ZEND_JMPZ:
- case ZEND_JMPZNZ:
- case ZEND_JMPZ_EX:
- {
- if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
- scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
- scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
- return;
- }
- s = Z_TYPE(zv) == IS_TRUE;
- break;
- }
- case ZEND_JMPNZ:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- {
- if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
- scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
- scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
- return;
- }
- s = Z_TYPE(zv) == IS_FALSE;
- break;
- }
- case ZEND_COALESCE:
- s = (Z_TYPE_P(op1) == IS_NULL);
- break;
- case ZEND_JMP_NULL:
- s = (Z_TYPE_P(op1) != IS_NULL);
- break;
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- /* A non-empty partial array is definitely non-empty, but an
- * empty partial array may be non-empty at runtime. */
- if (Z_TYPE_P(op1) != IS_ARRAY ||
- (IS_PARTIAL_ARRAY(op1) && zend_hash_num_elements(Z_ARR_P(op1)) == 0)) {
- scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
- scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
- return;
- }
- s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
- break;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- {
- zend_bool strict_comparison = opline->opcode == ZEND_MATCH;
- zend_uchar type = Z_TYPE_P(op1);
- zend_bool correct_type =
- (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
- || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
- || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
-
- if (correct_type) {
- zend_op_array *op_array = scdf->op_array;
- zend_ssa *ssa = scdf->ssa;
- HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
- zval *jmp_zv = type == IS_LONG
- ? zend_hash_index_find(jmptable, Z_LVAL_P(op1))
- : zend_hash_find(jmptable, Z_STR_P(op1));
- int target;
-
- if (jmp_zv) {
- target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))];
- } else {
- target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
- }
- scdf_mark_edge_feasible(scdf, block_num, target);
- return;
- } else if (strict_comparison) {
- zend_op_array *op_array = scdf->op_array;
- zend_ssa *ssa = scdf->ssa;
- int target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
- scdf_mark_edge_feasible(scdf, block_num, target);
- return;
- }
- s = 0;
- break;
- }
- default:
- for (s = 0; s < block->successors_count; s++) {
- scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
- }
- return;
- }
- scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
-}
-
-static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2)
-{
- zend_ulong index;
- zend_string *key;
- zval *val1, *val2;
-
- ZEND_HASH_FOREACH_KEY_VAL(ht1, index, key, val1) {
- if (key) {
- val2 = zend_hash_find(ht2, key);
- } else {
- val2 = zend_hash_index_find(ht2, index);
- }
- if (val2 && zend_is_identical(val1, val2)) {
- if (key) {
- val1 = zend_hash_add_new(ret, key, val1);
- } else {
- val1 = zend_hash_index_add_new(ret, index, val1);
- }
- Z_TRY_ADDREF_P(val1);
- }
- } ZEND_HASH_FOREACH_END();
-}
-
-static int join_partial_arrays(zval *a, zval *b)
-{
- zval ret;
-
- if ((Z_TYPE_P(a) != IS_ARRAY && !IS_PARTIAL_ARRAY(a))
- || (Z_TYPE_P(b) != IS_ARRAY && !IS_PARTIAL_ARRAY(b))) {
- return FAILURE;
- }
-
- empty_partial_array(&ret);
- join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
- zval_ptr_dtor_nogc(a);
- ZVAL_COPY_VALUE(a, &ret);
-
- return SUCCESS;
-}
-
-static int join_partial_objects(zval *a, zval *b)
-{
- zval ret;
-
- if (!IS_PARTIAL_OBJECT(a) || !IS_PARTIAL_OBJECT(b)) {
- return FAILURE;
- }
-
- empty_partial_object(&ret);
- join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
- zval_ptr_dtor_nogc(a);
- ZVAL_COPY_VALUE(a, &ret);
-
- return SUCCESS;
-}
-
-static void join_phi_values(zval *a, zval *b, zend_bool escape) {
- if (IS_BOT(a) || IS_TOP(b)) {
- return;
- }
- if (IS_TOP(a)) {
- zval_ptr_dtor_nogc(a);
- ZVAL_COPY(a, b);
- return;
- }
- if (IS_BOT(b)) {
- zval_ptr_dtor_nogc(a);
- MAKE_BOT(a);
- return;
- }
- if (IS_PARTIAL_ARRAY(a) || IS_PARTIAL_ARRAY(b)) {
- if (join_partial_arrays(a, b) != SUCCESS) {
- zval_ptr_dtor_nogc(a);
- MAKE_BOT(a);
- }
- } else if (IS_PARTIAL_OBJECT(a) || IS_PARTIAL_OBJECT(b)) {
- if (escape || join_partial_objects(a, b) != SUCCESS) {
- zval_ptr_dtor_nogc(a);
- MAKE_BOT(a);
- }
- } else if (!zend_is_identical(a, b)) {
- if (join_partial_arrays(a, b) != SUCCESS) {
- zval_ptr_dtor_nogc(a);
- MAKE_BOT(a);
- }
- }
-}
-
-static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
- sccp_ctx *ctx = (sccp_ctx *) scdf;
- zend_ssa *ssa = scdf->ssa;
- ZEND_ASSERT(phi->ssa_var >= 0);
- if (!IS_BOT(&ctx->values[phi->ssa_var])) {
- zend_basic_block *block = &ssa->cfg.blocks[phi->block];
- int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
-
- int i;
- zval result;
- MAKE_TOP(&result);
-#if SCP_DEBUG
- fprintf(stderr, "Handling phi(");
-#endif
- if (phi->pi >= 0) {
- ZEND_ASSERT(phi->sources[0] >= 0);
- if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) {
- join_phi_values(&result, &ctx->values[phi->sources[0]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
- }
- } else {
- for (i = 0; i < block->predecessors_count; i++) {
- ZEND_ASSERT(phi->sources[i] >= 0);
- if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
-#if SCP_DEBUG
- scp_dump_value(&ctx->values[phi->sources[i]]);
- fprintf(stderr, ",");
-#endif
- join_phi_values(&result, &ctx->values[phi->sources[i]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
- } else {
-#if SCP_DEBUG
- fprintf(stderr, " --,");
-#endif
- }
- }
- }
-#if SCP_DEBUG
- fprintf(stderr, ")\n");
-#endif
-
- set_value(scdf, ctx, phi->ssa_var, &result);
- zval_ptr_dtor_nogc(&result);
- }
-}
-
-static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
- zend_ssa *ssa = ctx->scdf.ssa;
- zend_ssa_var_info *info = &ssa->var_info[var_num];
-
- if (info->type & MAY_BE_UNDEF) {
- return NULL;
- }
-
- if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) {
- ZVAL_NULL(tmp);
- return tmp;
- }
- if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) {
- ZVAL_FALSE(tmp);
- return tmp;
- }
- if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) {
- ZVAL_TRUE(tmp);
- return tmp;
- }
-
- if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))
- && info->has_range
- && !info->range.overflow && !info->range.underflow
- && info->range.min == info->range.max) {
- ZVAL_LONG(tmp, info->range.min);
- return tmp;
- }
-
- return NULL;
-}
-
-/* Call instruction -> remove opcodes that are part of the call */
-static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
-{
- zend_ssa *ssa = ctx->scdf.ssa;
- zend_op_array *op_array = ctx->scdf.op_array;
- zend_call_info *call;
- int i;
-
- ZEND_ASSERT(ctx->call_map);
- call = ctx->call_map[opline - op_array->opcodes];
- ZEND_ASSERT(call);
- ZEND_ASSERT(call->caller_call_opline == opline);
- zend_ssa_remove_instr(ssa, opline, ssa_op);
- zend_ssa_remove_instr(ssa, call->caller_init_opline,
- &ssa->ops[call->caller_init_opline - op_array->opcodes]);
-
- for (i = 0; i < call->num_args; i++) {
- zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
- &ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
- }
-
- // TODO: remove call_info completely???
- call->callee_func = NULL;
-
- return call->num_args + 2;
-}
-
-/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
- * value(s) were determined by SCCP. It removes dead computational instructions and converts
- * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
- * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
- * must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
- * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
- * and can't be DCEd. This means that it will not be able collect all instructions rendered dead
- * by SCCP, because they may have potentially side-effecting types, but the actual values are
- * not. As such doing DCE here will allow us to eliminate more dead code in combination.
- * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
- * we need to collect.
- * d) The ordinary DCE pass cannot collect construction of dead non-escaping arrays and objects.
- */
-static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value)
-{
- zend_ssa *ssa = ctx->scdf.ssa;
- zend_op_array *op_array = ctx->scdf.op_array;
- int removed_ops = 0;
-
- if (var->definition >= 0) {
- zend_op *opline = &op_array->opcodes[var->definition];
- zend_ssa_op *ssa_op = &ssa->ops[var->definition];
-
- if (opline->opcode == ZEND_ASSIGN) {
- /* Leave assigns to DCE (due to dtor effects) */
- return 0;
- }
-
- if (ssa_op->result_def == var_num) {
- if (ssa_op->op1_def >= 0
- || ssa_op->op2_def >= 0) {
- /* we cannot remove instruction that defines other variables */
- return 0;
- } else if (opline->opcode == ZEND_JMPZ_EX
- || opline->opcode == ZEND_JMPNZ_EX
- || opline->opcode == ZEND_JMP_SET
- || opline->opcode == ZEND_COALESCE
- || opline->opcode == ZEND_JMP_NULL
- || opline->opcode == ZEND_FE_RESET_R
- || opline->opcode == ZEND_FE_RESET_RW
- || opline->opcode == ZEND_FE_FETCH_R
- || opline->opcode == ZEND_FE_FETCH_RW
- || opline->opcode == ZEND_NEW) {
- /* we cannot simple remove jump instructions */
- return 0;
- } else if (var->use_chain >= 0
- || var->phi_use_chain != NULL) {
- if (value
- && (opline->result_type & (IS_VAR|IS_TMP_VAR))
- && opline->opcode != ZEND_QM_ASSIGN
- && opline->opcode != ZEND_ROPE_INIT
- && opline->opcode != ZEND_ROPE_ADD
- && opline->opcode != ZEND_INIT_ARRAY
- && opline->opcode != ZEND_ADD_ARRAY_ELEMENT
- && opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
- /* Replace with QM_ASSIGN */
- zend_uchar old_type = opline->result_type;
- uint32_t old_var = opline->result.var;
-
- ssa_op->result_def = -1;
- if (opline->opcode == ZEND_DO_ICALL) {
- removed_ops = remove_call(ctx, opline, ssa_op) - 1;
- } else {
- zend_ssa_remove_instr(ssa, opline, ssa_op);
- }
- ssa_op->result_def = var_num;
- opline->opcode = ZEND_QM_ASSIGN;
- opline->result_type = old_type;
- opline->result.var = old_var;
- Z_TRY_ADDREF_P(value);
- zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, value);
- }
- return 0;
- } else {
- zend_ssa_remove_result_def(ssa, ssa_op);
- if (opline->opcode == ZEND_DO_ICALL) {
- removed_ops = remove_call(ctx, opline, ssa_op);
- } else if (opline->opcode == ZEND_TYPE_CHECK
- && (opline->op1_type & (IS_VAR|IS_TMP_VAR))
- && !value_known(&ctx->values[ssa_op->op1_use])) {
- /* For TYPE_CHECK we may compute the result value without knowing the
- * operand, based on type inference information. Make sure the operand is
- * freed and leave further cleanup to DCE. */
- opline->opcode = ZEND_FREE;
- opline->result_type = IS_UNUSED;
- removed_ops++;
- } else {
- zend_ssa_remove_instr(ssa, opline, ssa_op);
- removed_ops++;
- }
- }
- } else if (ssa_op->op1_def == var_num) {
- /* Compound assign or incdec -> convert to direct ASSIGN */
-
- if (!value) {
- /* In some cases zend_may_throw() may be avoided */
- switch (opline->opcode) {
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- if ((ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use]))
- || ((ssa_op+1)->op1_use >= 0 &&!value_known(&ctx->values[(ssa_op+1)->op1_use]))) {
- return 0;
- }
- break;
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- if (ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) {
- return 0;
- }
- break;
- default:
- if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
- return 0;
- }
- break;
- }
- }
-
- /* Mark result unused, if possible */
- if (ssa_op->result_def >= 0) {
- if (ssa->vars[ssa_op->result_def].use_chain < 0
- && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
- zend_ssa_remove_result_def(ssa, ssa_op);
- opline->result_type = IS_UNUSED;
- } else if (opline->opcode != ZEND_PRE_INC &&
- opline->opcode != ZEND_PRE_DEC) {
- /* op1_def and result_def are different */
- return removed_ops;
- }
- }
-
- /* Destroy previous op2 */
- if (opline->op2_type == IS_CONST) {
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- } else if (ssa_op->op2_use >= 0) {
- if (ssa_op->op2_use != ssa_op->op1_use) {
- zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use);
- }
- ssa_op->op2_use = -1;
- ssa_op->op2_use_chain = -1;
- }
-
- /* Remove OP_DATA opcode */
- switch (opline->opcode) {
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- removed_ops++;
- zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
- break;
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- removed_ops++;
- zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
- break;
- default:
- break;
- }
-
- if (value) {
- /* Convert to ASSIGN */
- opline->opcode = ZEND_ASSIGN;
- opline->op2_type = IS_CONST;
- opline->op2.constant = zend_optimizer_add_literal(op_array, value);
- Z_TRY_ADDREF_P(value);
- } else {
- /* Remove dead array or object construction */
- removed_ops++;
- if (var->use_chain >= 0 || var->phi_use_chain != NULL) {
- zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
- }
- zend_ssa_remove_op1_def(ssa, ssa_op);
- zend_ssa_remove_instr(ssa, opline, ssa_op);
- }
- }
- } else if (var->definition_phi
- && var->use_chain < 0
- && var->phi_use_chain == NULL) {
- zend_ssa_remove_phi(ssa, var->definition_phi);
- }
- return removed_ops;
-}
-
-/* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
- * can be replaced, because some instructions don't accept constant operands or only accept them
- * if they have a certain type. */
-static int replace_constant_operands(sccp_ctx *ctx) {
- zend_ssa *ssa = ctx->scdf.ssa;
- zend_op_array *op_array = ctx->scdf.op_array;
- int i;
- zval tmp;
- int removed_ops = 0;
-
- /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
- * and INIT_ARRAY. */
- for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) {
- zend_ssa_var *var = &ssa->vars[i];
- zval *value;
- int use;
-
- if (IS_PARTIAL_ARRAY(&ctx->values[i])
- || IS_PARTIAL_OBJECT(&ctx->values[i])) {
- if (!Z_DELREF(ctx->values[i])) {
- zend_array_destroy(Z_ARR(ctx->values[i]));
- }
- MAKE_BOT(&ctx->values[i]);
- if ((var->use_chain < 0 && var->phi_use_chain == NULL) || var->no_val) {
- removed_ops += try_remove_definition(ctx, i, var, NULL);
- }
- continue;
- } else if (value_known(&ctx->values[i])) {
- value = &ctx->values[i];
- } else {
- value = value_from_type_and_range(ctx, i, &tmp);
- if (!value) {
- continue;
- }
- }
-
- FOREACH_USE(var, use) {
- zend_op *opline = &op_array->opcodes[use];
- zend_ssa_op *ssa_op = &ssa->ops[use];
- if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
- if (opline->opcode == ZEND_NOP) {
- removed_ops++;
- }
- ZEND_ASSERT(ssa_op->op1_def == -1);
- if (ssa_op->op1_use != ssa_op->op2_use) {
- zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use);
- } else {
- ssa_op->op2_use_chain = ssa_op->op1_use_chain;
- }
- ssa_op->op1_use = -1;
- ssa_op->op1_use_chain = -1;
- }
- if (try_replace_op2(ctx, opline, ssa_op, i, value)) {
- ZEND_ASSERT(ssa_op->op2_def == -1);
- if (ssa_op->op2_use != ssa_op->op1_use) {
- zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use);
- }
- ssa_op->op2_use = -1;
- ssa_op->op2_use_chain = -1;
- }
- } FOREACH_USE_END();
-
- if (value_known(&ctx->values[i])) {
- removed_ops += try_remove_definition(ctx, i, var, value);
- }
- }
-
- return removed_ops;
-}
-
-static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp,
- zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) {
- int i;
- sccp->call_map = call_map;
- sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count);
-
- MAKE_TOP(&sccp->top);
- MAKE_BOT(&sccp->bot);
-
- i = 0;
- for (; i < op_array->last_var; ++i) {
- /* These are all undefined variables, which we have to mark BOT.
- * Otherwise the undefined variable warning might not be preserved. */
- MAKE_BOT(&sccp->values[i]);
- }
- for (; i < ssa->vars_count; ++i) {
- if (ssa->vars[i].alias) {
- MAKE_BOT(&sccp->values[i]);
- } else {
- MAKE_TOP(&sccp->values[i]);
- }
- }
-}
-
-static void sccp_context_free(sccp_ctx *sccp) {
- int i;
- for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) {
- zval_ptr_dtor_nogc(&sccp->values[i]);
- }
-}
-
-int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
-{
- sccp_ctx sccp;
- int removed_ops = 0;
- void *checkpoint = zend_arena_checkpoint(ctx->arena);
-
- sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
-
- sccp.scdf.handlers.visit_instr = sccp_visit_instr;
- sccp.scdf.handlers.visit_phi = sccp_visit_phi;
- sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
-
- scdf_init(ctx, &sccp.scdf, op_array, ssa);
- scdf_solve(&sccp.scdf, "SCCP");
-
- if (ctx->debug_level & ZEND_DUMP_SCCP) {
- int i, first = 1;
-
- for (i = op_array->last_var; i < ssa->vars_count; i++) {
- zval *zv = &sccp.values[i];
-
- if (IS_TOP(zv) || IS_BOT(zv)) {
- continue;
- }
- if (first) {
- first = 0;
- fprintf(stderr, "\nSCCP Values for \"");
- zend_dump_op_array_name(op_array);
- fprintf(stderr, "\":\n");
- }
- fprintf(stderr, " #%d.", i);
- zend_dump_var(op_array, IS_CV, ssa->vars[i].var);
- fprintf(stderr, " =");
- scp_dump_value(zv);
- fprintf(stderr, "\n");
- }
- }
-
- removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf);
- removed_ops += replace_constant_operands(&sccp);
-
- sccp_context_free(&sccp);
- zend_arena_release(&ctx->arena, checkpoint);
-
- return removed_ops;
-}
diff --git a/ext/opcache/Optimizer/scdf.c b/ext/opcache/Optimizer/scdf.c
deleted file mode 100644
index aa7ea3a1a2..0000000000
--- a/ext/opcache/Optimizer/scdf.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Sparse Conditional Data Flow Propagation Framework |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Nikita Popov <nikic@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "ZendAccelerator.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "Optimizer/scdf.h"
-
-/* This defines a generic framework for sparse conditional dataflow propagation. The algorithm is
- * based on "Sparse conditional constant propagation" by Wegman and Zadeck. We're using a
- * generalized implementation as described in chapter 8.3 of the SSA book.
- *
- * Every SSA variable is associated with an element on a finite-height lattice, those value can only
- * ever be lowered during the operation of the algorithm. If a value is lowered all instructions and
- * phis using that value need to be reconsidered (this is done by adding the variable to a
- * worklist). For phi functions the result is computed by applying the meet operation to the
- * operands. This continues until a fixed point is reached.
- *
- * The algorithm is control-flow sensitive: All blocks except the start block are initially assumed
- * to be unreachable. When considering a branch instruction, we determine the feasible successors
- * based on the current state of the variable lattice. If a new edge becomes feasible we either have
- * to mark the successor block executable and consider all instructions in it, or, if the target is
- * already executable, we only have to reconsider the phi functions (as we only consider phi
- * operands which are associated with a feasible edge).
- *
- * The generic framework requires the definition of three functions:
- * * visit_instr() should recompute the lattice values of all SSA variables defined by an
- * instruction.
- * * visit_phi() should recompute the lattice value of the SSA variable defined by the phi. While
- * doing this it should only consider operands for which scfg_is_edge_feasible() returns true.
- * * get_feasible_successors() should determine the feasible successors for a branch instruction.
- * Note that this callback only needs to handle conditional branches (with two successors).
- */
-
-#if 0
-#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
-#else
-#define DEBUG_PRINT(...)
-#endif
-
-void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) {
- uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
-
- if (zend_bitset_in(scdf->feasible_edges, edge)) {
- /* We already handled this edge */
- return;
- }
-
- DEBUG_PRINT("Marking edge %d->%d feasible\n", from, to);
- zend_bitset_incl(scdf->feasible_edges, edge);
-
- if (!zend_bitset_in(scdf->executable_blocks, to)) {
- if (!zend_bitset_in(scdf->block_worklist, to)) {
- DEBUG_PRINT("Adding block %d to worklist\n", to);
- }
- zend_bitset_incl(scdf->block_worklist, to);
- } else {
- /* Block is already executable, only a new edge became feasible.
- * Reevaluate phi nodes to account for changed source operands. */
- zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
- zend_ssa_phi *phi;
- for (phi = ssa_block->phis; phi; phi = phi->next) {
- zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
- scdf->handlers.visit_phi(scdf, phi);
- }
- }
-}
-
-void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa) {
- scdf->op_array = op_array;
- scdf->ssa = ssa;
-
- scdf->instr_worklist_len = zend_bitset_len(op_array->last);
- scdf->phi_var_worklist_len = zend_bitset_len(ssa->vars_count);
- scdf->block_worklist_len = zend_bitset_len(ssa->cfg.blocks_count);
-
- scdf->instr_worklist = zend_arena_calloc(&ctx->arena,
- scdf->instr_worklist_len + scdf->phi_var_worklist_len + 2 * scdf->block_worklist_len + zend_bitset_len(ssa->cfg.edges_count),
- sizeof(zend_ulong));
-
- scdf->phi_var_worklist = scdf->instr_worklist + scdf->instr_worklist_len;
- scdf->block_worklist = scdf->phi_var_worklist + scdf->phi_var_worklist_len;
- scdf->executable_blocks = scdf->block_worklist + scdf->block_worklist_len;
- scdf->feasible_edges = scdf->executable_blocks + scdf->block_worklist_len;
-
- zend_bitset_incl(scdf->block_worklist, 0);
- zend_bitset_incl(scdf->executable_blocks, 0);
-}
-
-void scdf_solve(scdf_ctx *scdf, const char *name) {
- zend_ssa *ssa = scdf->ssa;
- DEBUG_PRINT("Start SCDF solve (%s)\n", name);
- while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len)
- || !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len)
- || !zend_bitset_empty(scdf->block_worklist, scdf->block_worklist_len)
- ) {
- int i;
- while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) {
- zend_ssa_phi *phi = ssa->vars[i].definition_phi;
- ZEND_ASSERT(phi);
- if (zend_bitset_in(scdf->executable_blocks, phi->block)) {
- scdf->handlers.visit_phi(scdf, phi);
- }
- }
-
- while ((i = zend_bitset_pop_first(scdf->instr_worklist, scdf->instr_worklist_len)) >= 0) {
- int block_num = ssa->cfg.map[i];
- if (zend_bitset_in(scdf->executable_blocks, block_num)) {
- zend_basic_block *block = &ssa->cfg.blocks[block_num];
- zend_op *opline = &scdf->op_array->opcodes[i];
- zend_ssa_op *ssa_op = &ssa->ops[i];
- if (opline->opcode == ZEND_OP_DATA) {
- opline--;
- ssa_op--;
- }
- scdf->handlers.visit_instr(scdf, opline, ssa_op);
- if (i == block->start + block->len - 1) {
- if (block->successors_count == 1) {
- scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
- } else if (block->successors_count > 1) {
- scdf->handlers.mark_feasible_successors(scdf, block_num, block, opline, ssa_op);
- }
- }
- }
- }
-
- while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) {
- /* This block is now live. Interpret phis and instructions in it. */
- zend_basic_block *block = &ssa->cfg.blocks[i];
- zend_ssa_block *ssa_block = &ssa->blocks[i];
-
- DEBUG_PRINT("Pop block %d from worklist\n", i);
- zend_bitset_incl(scdf->executable_blocks, i);
-
- {
- zend_ssa_phi *phi;
- for (phi = ssa_block->phis; phi; phi = phi->next) {
- zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
- scdf->handlers.visit_phi(scdf, phi);
- }
- }
-
- if (block->len == 0) {
- /* Zero length blocks don't have a last instruction that would normally do this */
- scdf_mark_edge_feasible(scdf, i, block->successors[0]);
- } else {
- zend_op *opline = NULL;
- int j, end = block->start + block->len;
- for (j = block->start; j < end; j++) {
- opline = &scdf->op_array->opcodes[j];
- zend_bitset_excl(scdf->instr_worklist, j);
- if (opline->opcode != ZEND_OP_DATA) {
- scdf->handlers.visit_instr(scdf, opline, &ssa->ops[j]);
- }
- }
- if (block->successors_count == 1) {
- scdf_mark_edge_feasible(scdf, i, block->successors[0]);
- } else if (block->successors_count > 1) {
- ZEND_ASSERT(opline && "Should have opline in non-empty block");
- if (opline->opcode == ZEND_OP_DATA) {
- opline--;
- j--;
- }
- scdf->handlers.mark_feasible_successors(scdf, i, block, opline, &ssa->ops[j-1]);
- }
- }
- }
- }
-}
-
-/* If a live range starts in a reachable block and ends in an unreachable block, we should
- * not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
- * is necessary for the correctness of temporary compaction. */
-static zend_bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) {
- uint32_t i;
- const zend_op_array *op_array = scdf->op_array;
- const zend_cfg *cfg = &scdf->ssa->cfg;
- const zend_basic_block *block = &cfg->blocks[block_idx];
- if (!(cfg->flags & ZEND_FUNC_FREE_LOOP_VAR)) {
- return 0;
- }
- for (i = block->start; i < block->start + block->len; i++) {
- zend_op *opline = &op_array->opcodes[i];
- if (zend_optimizer_is_loop_var_free(opline)) {
- int ssa_var = scdf->ssa->ops[i].op1_use;
- if (ssa_var >= 0) {
- int op_num = scdf->ssa->vars[ssa_var].definition;
- uint32_t def_block;
- ZEND_ASSERT(op_num >= 0);
- def_block = cfg->map[op_num];
- if (zend_bitset_in(scdf->executable_blocks, def_block)) {
- return 1;
- }
- }
- }
- }
- return 0;
-}
-
-/* Removes unreachable blocks. This will remove both the instructions (and phis) in the
- * blocks, as well as remove them from the successor / predecessor lists and mark them
- * unreachable. Blocks already marked unreachable are not removed. */
-int scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
- zend_ssa *ssa = scdf->ssa;
- int i;
- int removed_ops = 0;
- for (i = 0; i < ssa->cfg.blocks_count; i++) {
- if (!zend_bitset_in(scdf->executable_blocks, i)
- && (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE)
- && !kept_alive_by_loop_var_free(scdf, i)) {
- removed_ops += ssa->cfg.blocks[i].len;
- zend_ssa_remove_block(scdf->op_array, ssa, i);
- }
- }
- return removed_ops;
-}
diff --git a/ext/opcache/Optimizer/scdf.h b/ext/opcache/Optimizer/scdf.h
deleted file mode 100644
index 64b4b61340..0000000000
--- a/ext/opcache/Optimizer/scdf.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Call Graph |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Nikita Popov <nikic@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef _SCDF_H
-#define _SCDF_H
-
-#include "zend_bitset.h"
-
-typedef struct _scdf_ctx {
- zend_op_array *op_array;
- zend_ssa *ssa;
- zend_bitset instr_worklist;
- /* Represent phi-instructions through the defining var */
- zend_bitset phi_var_worklist;
- zend_bitset block_worklist;
- zend_bitset executable_blocks;
- /* 1 bit per edge, see scdf_edge(cfg, from, to) */
- zend_bitset feasible_edges;
- uint32_t instr_worklist_len;
- uint32_t phi_var_worklist_len;
- uint32_t block_worklist_len;
-
- struct {
- void (*visit_instr)(
- struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op);
- void (*visit_phi)(
- struct _scdf_ctx *scdf, zend_ssa_phi *phi);
- void (*mark_feasible_successors)(
- struct _scdf_ctx *scdf, int block_num, zend_basic_block *block,
- zend_op *opline, zend_ssa_op *ssa_op);
- } handlers;
-} scdf_ctx;
-
-void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa);
-void scdf_solve(scdf_ctx *scdf, const char *name);
-
-int scdf_remove_unreachable_blocks(scdf_ctx *scdf);
-
-/* Add uses to worklist */
-static inline void scdf_add_to_worklist(scdf_ctx *scdf, int var_num) {
- zend_ssa *ssa = scdf->ssa;
- zend_ssa_var *var = &ssa->vars[var_num];
- int use;
- zend_ssa_phi *phi;
- FOREACH_USE(var, use) {
- zend_bitset_incl(scdf->instr_worklist, use);
- } FOREACH_USE_END();
- FOREACH_PHI_USE(var, phi) {
- zend_bitset_incl(scdf->phi_var_worklist, phi->ssa_var);
- } FOREACH_PHI_USE_END();
-}
-
-/* This should usually not be necessary, however it's used for type narrowing. */
-static inline void scdf_add_def_to_worklist(scdf_ctx *scdf, int var_num) {
- zend_ssa_var *var = &scdf->ssa->vars[var_num];
- if (var->definition >= 0) {
- zend_bitset_incl(scdf->instr_worklist, var->definition);
- } else if (var->definition_phi) {
- zend_bitset_incl(scdf->phi_var_worklist, var_num);
- }
-}
-
-static inline uint32_t scdf_edge(zend_cfg *cfg, int from, int to) {
- zend_basic_block *to_block = cfg->blocks + to;
- int i;
-
- for (i = 0; i < to_block->predecessors_count; i++) {
- uint32_t edge = to_block->predecessor_offset + i;
-
- if (cfg->predecessors[edge] == from) {
- return edge;
- }
- }
- ZEND_UNREACHABLE();
-}
-
-static inline zend_bool scdf_is_edge_feasible(scdf_ctx *scdf, int from, int to) {
- uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
- return zend_bitset_in(scdf->feasible_edges, edge);
-}
-
-void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to);
-
-#endif
diff --git a/ext/opcache/Optimizer/ssa_integrity.c b/ext/opcache/Optimizer/ssa_integrity.c
deleted file mode 100644
index 5cb383c77a..0000000000
--- a/ext/opcache/Optimizer/ssa_integrity.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, SSA validation |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Nikita Popov <nikic@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "ZendAccelerator.h"
-#include "Optimizer/zend_optimizer_internal.h"
-
-/* The ssa_verify_integrity() function ensures that that certain invariants of the SSA form and
- * CFG are upheld and prints messages to stderr if this is not the case. */
-
-static inline zend_bool is_in_use_chain(zend_ssa *ssa, int var, int check) {
- int use;
- FOREACH_USE(&ssa->vars[var], use) {
- if (use == check) {
- return 1;
- }
- } FOREACH_USE_END();
- return 0;
-}
-
-static inline zend_bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) {
- zend_ssa_phi *phi;
- FOREACH_PHI_USE(&ssa->vars[var], phi) {
- if (phi == check) {
- return 1;
- }
- } FOREACH_PHI_USE_END();
- return 0;
-}
-
-static inline zend_bool is_used_by_op(zend_ssa *ssa, int op, int check) {
- zend_ssa_op *ssa_op = &ssa->ops[op];
- return (ssa_op->op1_use == check)
- || (ssa_op->op2_use == check)
- || (ssa_op->result_use == check);
-}
-
-static inline zend_bool is_defined_by_op(zend_ssa *ssa, int op, int check) {
- zend_ssa_op *ssa_op = &ssa->ops[op];
- return (ssa_op->op1_def == check)
- || (ssa_op->op2_def == check)
- || (ssa_op->result_def == check);
-}
-
-static inline zend_bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check) {
- int source;
- FOREACH_PHI_SOURCE(phi, source) {
- if (source == check) {
- return 1;
- }
- } FOREACH_PHI_SOURCE_END();
- return 0;
-}
-
-static inline zend_bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) {
- int i, *predecessors = &cfg->predecessors[block->predecessor_offset];
- for (i = 0; i < block->predecessors_count; i++) {
- if (predecessors[i] == check) {
- return 1;
- }
- }
- return 0;
-}
-
-static inline zend_bool is_in_successors(zend_basic_block *block, int check) {
- int s;
- for (s = 0; s < block->successors_count; s++) {
- if (block->successors[s] == check) {
- return 1;
- }
- }
- return 0;
-}
-
-static inline zend_bool is_var_type(zend_uchar type) {
- return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0;
-}
-
-#define FAIL(...) do { \
- if (status == SUCCESS) { \
- fprintf(stderr, "\nIn function %s::%s (%s):\n", \
- op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", \
- op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", extra); \
- } \
- fprintf(stderr, __VA_ARGS__); \
- status = FAILURE; \
-} while (0)
-
-#define VARFMT "%d (%s%s)"
-#define VAR(i) \
- (i), (ssa->vars[i].var < op_array->last_var ? "CV $" : "TMP"), \
- (ssa->vars[i].var < op_array->last_var ? ZSTR_VAL(op_array->vars[ssa->vars[i].var]) : "")
-
-#define INSTRFMT "%d (%s)"
-#define INSTR(i) \
- (i), (zend_get_opcode_name(op_array->opcodes[i].opcode))
-
-int ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra) {
- zend_cfg *cfg = &ssa->cfg;
- zend_ssa_phi *phi;
- int i, status = SUCCESS;
-
- /* Vars */
- for (i = 0; i < ssa->vars_count; i++) {
- zend_ssa_var *var = &ssa->vars[i];
- int use, c;
- uint32_t type = ssa->var_info[i].type;
-
- if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) {
- if (var->use_chain >= 0) {
- FAIL("var " VARFMT " without def has op uses\n", VAR(i));
- }
- if (var->phi_use_chain) {
- FAIL("var " VARFMT " without def has phi uses\n", VAR(i));
- }
- }
- if (var->definition >= 0 && var->definition_phi) {
- FAIL("var " VARFMT " has both def and def_phi\n", VAR(i));
- }
- if (var->definition >= 0) {
- if (!is_defined_by_op(ssa, var->definition, i)) {
- FAIL("var " VARFMT " not defined by op " INSTRFMT "\n",
- VAR(i), INSTR(var->definition));
- }
- }
- if (var->definition_phi) {
- if (var->definition_phi->ssa_var != i) {
- FAIL("var " VARFMT " not defined by given phi\n", VAR(i));
- }
- }
-
- c = 0;
- FOREACH_USE(var, use) {
- if (++c > 10000) {
- FAIL("cycle in uses of " VARFMT "\n", VAR(i));
- return status;
- }
- if (!is_used_by_op(ssa, use, i)) {
- fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use);
- }
- } FOREACH_USE_END();
-
- c = 0;
- FOREACH_PHI_USE(var, phi) {
- if (++c > 10000) {
- FAIL("cycle in phi uses of " VARFMT "\n", VAR(i));
- return status;
- }
- if (!is_in_phi_sources(ssa, phi, i)) {
- FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var);
- }
- } FOREACH_PHI_USE_END();
-
- if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) {
- FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
- }
- if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) {
- FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
- }
- }
-
- /* Instructions */
- FOREACH_INSTR_NUM(i) {
- zend_ssa_op *ssa_op = &ssa->ops[i];
- zend_op *opline = &op_array->opcodes[i];
- if (is_var_type(opline->op1_type)) {
- if (ssa_op->op1_use < 0 && ssa_op->op1_def < 0) {
- FAIL("var op1 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
- }
- } else {
- if (ssa_op->op1_use >= 0 || ssa_op->op1_def >= 0) {
- FAIL("non-var op1 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
- }
- }
- if (is_var_type(opline->op2_type)) {
- if (ssa_op->op2_use < 0 && ssa_op->op2_def < 0) {
- FAIL("var op2 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
- }
- } else {
- if (ssa_op->op2_use >= 0 || ssa_op->op2_def >= 0) {
- FAIL("non-var op2 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
- }
- }
- if (is_var_type(opline->result_type)) {
- if (ssa_op->result_use < 0 && ssa_op->result_def < 0) {
- FAIL("var result of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
- }
- } else {
- if (ssa_op->result_use >= 0 || ssa_op->result_def >= 0) {
- FAIL("non-var result of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
- }
- }
-
- if (ssa_op->op1_use >= 0) {
- if (ssa_op->op1_use >= ssa->vars_count) {
- FAIL("op1 use %d out of range\n", ssa_op->op1_use);
- }
- if (!is_in_use_chain(ssa, ssa_op->op1_use, i)) {
- FAIL("op1 use of " VARFMT " in " INSTRFMT " not in use chain\n",
- VAR(ssa_op->op1_use), INSTR(i));
- }
- if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_use].var) {
- FAIL("op1 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
- VAR(ssa_op->op1_use), VAR_NUM(opline->op1.var), INSTR(i));
- }
- }
- if (ssa_op->op2_use >= 0) {
- if (ssa_op->op2_use >= ssa->vars_count) {
- FAIL("op2 use %d out of range\n", ssa_op->op2_use);
- }
- if (!is_in_use_chain(ssa, ssa_op->op2_use, i)) {
- FAIL("op2 use of " VARFMT " in " INSTRFMT " not in use chain\n",
- VAR(ssa_op->op2_use), INSTR(i));
- }
- if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_use].var) {
- FAIL("op2 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
- VAR(ssa_op->op2_use), VAR_NUM(opline->op2.var), INSTR(i));
- }
- }
- if (ssa_op->result_use >= 0) {
- if (ssa_op->result_use >= ssa->vars_count) {
- FAIL("result use %d out of range\n", ssa_op->result_use);
- }
- if (!is_in_use_chain(ssa, ssa_op->result_use, i)) {
- FAIL("result use of " VARFMT " in " INSTRFMT " not in use chain\n",
- VAR(ssa_op->result_use), INSTR(i));
- }
- if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_use].var) {
- FAIL("result use of " VARFMT " does not match op %d of " INSTRFMT "\n",
- VAR(ssa_op->result_use), VAR_NUM(opline->result.var), INSTR(i));
- }
- }
- if (ssa_op->op1_def >= 0) {
- if (ssa_op->op1_def >= ssa->vars_count) {
- FAIL("op1 def %d out of range\n", ssa_op->op1_def);
- }
- if (ssa->vars[ssa_op->op1_def].definition != i) {
- FAIL("op1 def of " VARFMT " in " INSTRFMT " invalid\n",
- VAR(ssa_op->op1_def), INSTR(i));
- }
- if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_def].var) {
- FAIL("op1 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
- VAR(ssa_op->op1_def), VAR_NUM(opline->op1.var), INSTR(i));
- }
- }
- if (ssa_op->op2_def >= 0) {
- if (ssa_op->op2_def >= ssa->vars_count) {
- FAIL("op2 def %d out of range\n", ssa_op->op2_def);
- }
- if (ssa->vars[ssa_op->op2_def].definition != i) {
- FAIL("op2 def of " VARFMT " in " INSTRFMT " invalid\n",
- VAR(ssa_op->op2_def), INSTR(i));
- }
- if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_def].var) {
- FAIL("op2 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
- VAR(ssa_op->op2_def), VAR_NUM(opline->op2.var), INSTR(i));
- }
- }
- if (ssa_op->result_def >= 0) {
- if (ssa_op->result_def >= ssa->vars_count) {
- FAIL("result def %d out of range\n", ssa_op->result_def);
- }
- if (ssa->vars[ssa_op->result_def].definition != i) {
- FAIL("result def of " VARFMT " in " INSTRFMT " invalid\n",
- VAR(ssa_op->result_def), INSTR(i));
- }
- if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_def].var) {
- FAIL("result def of " VARFMT " does not match op %d of " INSTRFMT "\n",
- VAR(ssa_op->result_def), VAR_NUM(opline->result.var), INSTR(i));
- }
- }
- } FOREACH_INSTR_NUM_END();
-
- /* Phis */
- FOREACH_PHI(phi) {
- unsigned num_sources = NUM_PHI_SOURCES(phi);
- for (i = 0; i < num_sources; i++) {
- int source = phi->sources[i];
- if (source < 0) {
- FAIL(VARFMT " negative source\n", VAR(phi->ssa_var));
- }
- if (!is_in_phi_use_chain(ssa, source, phi)) {
- FAIL(VARFMT " not in phi use chain of %d\n", VAR(phi->ssa_var), source);
- }
- if (ssa->vars[source].var != ssa->vars[phi->ssa_var].var) {
- FAIL(VARFMT " source of phi for " VARFMT "\n", VAR(source), VAR(phi->ssa_var));
- }
- if (phi->use_chains[i]) {
- int j;
- for (j = i + 1; j < num_sources; j++) {
- if (phi->sources[j] == source && phi->use_chains[j]) {
- FAIL("use chain for source " VARFMT " of phi " VARFMT
- " at %d despite earlier use\n", VAR(source), VAR(phi->ssa_var), j);
- }
- }
- }
- }
- if (ssa->vars[phi->ssa_var].definition_phi != phi) {
- FAIL(VARFMT " does not define this phi\n", VAR(phi->ssa_var));
- }
- } FOREACH_PHI_END();
-
- /* Blocks */
- for (i = 0; i < cfg->blocks_count; i++) {
- zend_basic_block *block = &cfg->blocks[i];
- int *predecessors = &cfg->predecessors[block->predecessor_offset];
- int s, j;
-
- if (i != 0 && block->start < (block-1)->start + (block-1)->len) {
- FAIL("Block %d start %d smaller previous end %d\n",
- i, block->start, (block-1)->start + (block-1)->len);
- }
- if (i != cfg->blocks_count-1 && block->start + block->len > (block+1)->start) {
- FAIL("Block %d end %d greater next start %d\n",
- i, block->start + block->len, (block+1)->start);
- }
-
- for (j = block->start; j < block->start + block->len; j++) {
- if (cfg->map[j] != i) {
- FAIL("Instr " INSTRFMT " not associated with block %d\n", INSTR(j), i);
- }
- }
-
- if (!(block->flags & ZEND_BB_REACHABLE)) {
- if (ssa->blocks[i].phis) {
- FAIL("Unreachable block %d has phis\n", i);
- }
- continue;
- }
-
- for (s = 0; s < block->successors_count; s++) {
- zend_basic_block *next_block;
- if (block->successors[s] < 0) {
- FAIL("Successor number %d of %d negative", s, i);
- }
- next_block = &cfg->blocks[block->successors[s]];
- if (!(next_block->flags & ZEND_BB_REACHABLE)) {
- FAIL("Successor %d of %d not reachable\n", block->successors[s], i);
- }
- if (!is_in_predecessors(cfg, next_block, i)) {
- FAIL("Block %d predecessors missing %d\n", block->successors[s], i);
- }
- }
-
- for (j = 0; j < block->predecessors_count; j++) {
- if (predecessors[j] >= 0) {
- int k;
- zend_basic_block *prev_block = &cfg->blocks[predecessors[j]];
- if (!(prev_block->flags & ZEND_BB_REACHABLE)) {
- FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i);
- }
- if (!is_in_successors(prev_block, i)) {
- FAIL("Block %d successors missing %d\n", predecessors[j], i);
- }
- for (k = 0; k < block->predecessors_count; k++) {
- if (k != j && predecessors[k] == predecessors[j]) {
- FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]);
- }
- }
- }
- }
- }
-
- return status;
-}
diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c
deleted file mode 100644
index 0fa8945f09..0000000000
--- a/ext/opcache/Optimizer/zend_call_graph.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Call Graph |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "zend_compile.h"
-#include "zend_extensions.h"
-#include "Optimizer/zend_optimizer.h"
-#include "zend_optimizer_internal.h"
-#include "zend_inference.h"
-#include "zend_call_graph.h"
-#include "zend_func_info.h"
-#include "zend_inference.h"
-#include "zend_call_graph.h"
-
-static void zend_op_array_calc(zend_op_array *op_array, void *context)
-{
- zend_call_graph *call_graph = context;
- call_graph->op_arrays_count++;
-}
-
-static void zend_op_array_collect(zend_op_array *op_array, void *context)
-{
- zend_call_graph *call_graph = context;
- 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;
- call_graph->op_arrays_count++;
-}
-
-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_FLAG(use_heap);
- zend_bool is_prototype;
-
- call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap);
- call_info = NULL;
- while (opline != end) {
- switch (opline->opcode) {
- case ZEND_INIT_FCALL:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_STATIC_METHOD_CALL:
- call_stack[call] = call_info;
- func = zend_optimizer_get_called_func(
- script, op_array, opline, &is_prototype);
- /* TODO: Support prototypes? */
- if (func && !is_prototype) {
- 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_callee = func_info->callee_info;
- func_info->callee_info = call_info;
-
- if (build_flags & ZEND_CALL_TREE) {
- call_info->next_caller = NULL;
- } else if (func->type == ZEND_INTERNAL_FUNCTION) {
- call_info->next_caller = NULL;
- } else {
- zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array);
- if (callee_func_info) {
- call_info->next_caller = callee_func_info->caller_info;
- callee_func_info->caller_info = call_info;
- } else {
- call_info->next_caller = NULL;
- }
- }
- } else {
- call_info = NULL;
- }
- call++;
- break;
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_NEW:
- case ZEND_INIT_USER_CALL:
- call_stack[call] = call_info;
- call_info = NULL;
- 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;
- if (call_info) {
- call_info->caller_call_opline = opline;
- }
- call--;
- call_info = call_stack[call];
- break;
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- case ZEND_SEND_USER:
- if (call_info) {
- if (opline->op2_type == IS_CONST) {
- call_info->named_args = 1;
- break;
- }
-
- uint32_t num = opline->op2.num;
- if (num > 0) {
- num--;
- }
- call_info->arg_info[num].opline = opline;
- }
- break;
- case ZEND_SEND_ARRAY:
- case ZEND_SEND_UNPACK:
- if (call_info) {
- call_info->send_unpack = 1;
- }
- break;
- case ZEND_EXIT:
- /* In this case the DO_CALL opcode may have been dropped
- * and caller_call_opline will be NULL. */
- break;
- }
- opline++;
- }
- free_alloca(call_stack, use_heap);
- 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(zend_ulong) * 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;
- }
- }
-
- free_alloca(visited, use_heap);
-}
-
-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, zend_call_graph *call_graph) /* {{{ */
-{
- call_graph->op_arrays_count = 0;
- zend_foreach_op_array(script, zend_op_array_calc, call_graph);
-
- 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));
- call_graph->op_arrays_count = 0;
- zend_foreach_op_array(script, zend_op_array_collect, call_graph);
-
- return SUCCESS;
-}
-/* }}} */
-
-void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
-{
- int i;
-
- for (i = 0; i < call_graph->op_arrays_count; i++) {
- zend_analyze_calls(arena, script, 0, call_graph->op_arrays[i], call_graph->func_infos + i);
- }
- zend_analyze_recursion(call_graph);
- zend_sort_op_arrays(call_graph);
-}
-/* }}} */
-
-zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array) /* {{{ */
-{
- zend_call_info **map, *call;
- if (!info->callee_info) {
- /* Don't build call map if function contains no calls */
- return NULL;
- }
-
- map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last);
- for (call = info->callee_info; call; call = call->next_callee) {
- int i;
- map[call->caller_init_opline - op_array->opcodes] = call;
- if (call->caller_call_opline) {
- map[call->caller_call_opline - op_array->opcodes] = call;
- }
- for (i = 0; i < call->num_args; i++) {
- if (call->arg_info[i].opline) {
- map[call->arg_info[i].opline - op_array->opcodes] = call;
- }
- }
- }
- return map;
-}
-/* }}} */
diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h
deleted file mode 100644
index 28522a1277..0000000000
--- a/ext/opcache/Optimizer/zend_call_graph.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Call Graph |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#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;
-
-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_bool recursive;
- zend_bool send_unpack; /* Parameters passed by SEND_UNPACK or SEND_ARRAY */
- zend_bool named_args; /* Function has named arguments */
- 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 */
- zend_call_info **call_map; /* Call info associated with init/call/send opnum */
- zend_ssa_var_info return_info;
-};
-
-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, zend_call_graph *call_graph);
-void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
-zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array);
-int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info);
-
-END_EXTERN_C()
-
-#endif /* ZEND_CALL_GRAPH_H */
diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c
deleted file mode 100644
index 0560bcf2d5..0000000000
--- a/ext/opcache/Optimizer/zend_cfg.c
+++ /dev/null
@@ -1,909 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, CFG - Control Flow Graph |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "zend_compile.h"
-#include "zend_cfg.h"
-#include "zend_func_info.h"
-#include "zend_worklist.h"
-#include "zend_optimizer.h"
-#include "zend_optimizer_internal.h"
-
-static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
-{
- zend_basic_block *blocks = cfg->blocks;
-
- while (1) {
- int i;
-
- b->flags |= ZEND_BB_REACHABLE;
- if (b->successors_count == 0) {
- b->flags |= ZEND_BB_EXIT;
- return;
- }
-
- for (i = 0; i < b->successors_count; i++) {
- zend_basic_block *succ = blocks + b->successors[i];
-
- if (b->len != 0) {
- zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
- if (b->successors_count == 1) {
- if (opcode == ZEND_JMP) {
- succ->flags |= ZEND_BB_TARGET;
- } else {
- succ->flags |= ZEND_BB_FOLLOW;
-
- if ((cfg->flags & ZEND_CFG_STACKLESS)) {
- if (opcode == ZEND_INCLUDE_OR_EVAL ||
- opcode == ZEND_GENERATOR_CREATE ||
- opcode == ZEND_YIELD ||
- opcode == ZEND_YIELD_FROM ||
- opcode == ZEND_DO_FCALL ||
- opcode == ZEND_DO_UCALL ||
- opcode == ZEND_DO_FCALL_BY_NAME) {
- succ->flags |= ZEND_BB_ENTRY;
- }
- }
- if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
- if (opcode == ZEND_RECV ||
- opcode == ZEND_RECV_INIT) {
- succ->flags |= ZEND_BB_RECV_ENTRY;
- }
- }
- }
- } else if (b->successors_count == 2) {
- if (i == 0 || opcode == ZEND_JMPZNZ) {
- succ->flags |= ZEND_BB_TARGET;
- } else {
- succ->flags |= ZEND_BB_FOLLOW;
- }
- } else {
- ZEND_ASSERT(
- opcode == ZEND_SWITCH_LONG
- || opcode == ZEND_SWITCH_STRING
- || opcode == ZEND_MATCH
- );
- if (i == b->successors_count - 1) {
- succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
- } else {
- succ->flags |= ZEND_BB_TARGET;
- }
- }
- } else {
- succ->flags |= ZEND_BB_FOLLOW;
- }
-
- if (i == b->successors_count - 1) {
- /* Tail call optimization */
- if (succ->flags & ZEND_BB_REACHABLE) {
- return;
- }
-
- b = succ;
- break;
- } else {
- /* Recursively check reachability */
- if (!(succ->flags & ZEND_BB_REACHABLE)) {
- zend_mark_reachable(opcodes, cfg, succ);
- }
- }
- }
- }
-}
-/* }}} */
-
-static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
-{
- zend_basic_block *blocks = cfg->blocks;
-
- blocks[start].flags = ZEND_BB_START;
- zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
-
- if (op_array->last_try_catch) {
- zend_basic_block *b;
- int j, changed;
- uint32_t *block_map = cfg->map;
-
- do {
- changed = 0;
-
- /* Add exception paths */
- for (j = 0; j < op_array->last_try_catch; j++) {
-
- /* check for jumps into the middle of try block */
- b = blocks + block_map[op_array->try_catch_array[j].try_op];
- if (!(b->flags & ZEND_BB_REACHABLE)) {
- zend_basic_block *end;
-
- if (op_array->try_catch_array[j].catch_op) {
- end = blocks + block_map[op_array->try_catch_array[j].catch_op];
- while (b != end) {
- if (b->flags & ZEND_BB_REACHABLE) {
- op_array->try_catch_array[j].try_op = b->start;
- break;
- }
- b++;
- }
- }
- b = blocks + block_map[op_array->try_catch_array[j].try_op];
- if (!(b->flags & ZEND_BB_REACHABLE)) {
- if (op_array->try_catch_array[j].finally_op) {
- end = blocks + block_map[op_array->try_catch_array[j].finally_op];
- while (b != end) {
- if (b->flags & ZEND_BB_REACHABLE) {
- op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
- changed = 1;
- zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
- break;
- }
- b++;
- }
- }
- }
- }
-
- b = blocks + block_map[op_array->try_catch_array[j].try_op];
- if (b->flags & ZEND_BB_REACHABLE) {
- b->flags |= ZEND_BB_TRY;
- if (op_array->try_catch_array[j].catch_op) {
- b = blocks + block_map[op_array->try_catch_array[j].catch_op];
- b->flags |= ZEND_BB_CATCH;
- if (!(b->flags & ZEND_BB_REACHABLE)) {
- changed = 1;
- zend_mark_reachable(op_array->opcodes, cfg, b);
- }
- }
- if (op_array->try_catch_array[j].finally_op) {
- b = blocks + block_map[op_array->try_catch_array[j].finally_op];
- b->flags |= ZEND_BB_FINALLY;
- if (!(b->flags & ZEND_BB_REACHABLE)) {
- changed = 1;
- zend_mark_reachable(op_array->opcodes, cfg, b);
- }
- }
- if (op_array->try_catch_array[j].finally_end) {
- b = blocks + block_map[op_array->try_catch_array[j].finally_end];
- b->flags |= ZEND_BB_FINALLY_END;
- if (!(b->flags & ZEND_BB_REACHABLE)) {
- changed = 1;
- zend_mark_reachable(op_array->opcodes, cfg, b);
- }
- }
- } else {
- if (op_array->try_catch_array[j].catch_op) {
- ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
- }
- if (op_array->try_catch_array[j].finally_op) {
- ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
- }
- if (op_array->try_catch_array[j].finally_end) {
- ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
- }
- }
- }
- } while (changed);
- }
-
- if (cfg->flags & ZEND_FUNC_FREE_LOOP_VAR) {
- zend_basic_block *b;
- int j;
- uint32_t *block_map = cfg->map;
-
- /* Mark blocks that are unreachable, but free a loop var created in a reachable block. */
- for (b = blocks; b < blocks + cfg->blocks_count; b++) {
- if (b->flags & ZEND_BB_REACHABLE) {
- continue;
- }
-
- for (j = b->start; j < b->start + b->len; j++) {
- zend_op *opline = &op_array->opcodes[j];
- if (zend_optimizer_is_loop_var_free(opline)) {
- zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline);
- if (def_opline) {
- uint32_t def_block = block_map[def_opline - op_array->opcodes];
- if (blocks[def_block].flags & ZEND_BB_REACHABLE) {
- b->flags |= ZEND_BB_UNREACHABLE_FREE;
- break;
- }
- }
- }
- }
- }
- }
-}
-/* }}} */
-
-void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
-{
- zend_basic_block *blocks = cfg->blocks;
- int i;
- int start = 0;
-
- for (i = 0; i < cfg->blocks_count; i++) {
- if (blocks[i].flags & ZEND_BB_REACHABLE) {
- start = i;
- i++;
- break;
- }
- }
-
- /* clear all flags */
- for (i = 0; i < cfg->blocks_count; i++) {
- blocks[i].flags = 0;
- }
-
- zend_mark_reachable_blocks(op_array, cfg, start);
-}
-/* }}} */
-
-static void initialize_block(zend_basic_block *block) {
- block->flags = 0;
- block->successors = block->successors_storage;
- block->successors_count = 0;
- block->predecessors_count = 0;
- block->predecessor_offset = -1;
- block->idom = -1;
- block->loop_header = -1;
- block->level = -1;
- block->children = -1;
- block->next_child = -1;
-}
-
-#define BB_START(i) do { \
- if (!block_map[i]) { blocks_count++;} \
- block_map[i]++; \
- } while (0)
-
-int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
-{
- uint32_t flags = 0;
- uint32_t i;
- int j;
- uint32_t *block_map;
- zend_function *fn;
- int blocks_count = 0;
- zend_basic_block *blocks;
- zval *zv;
- zend_bool extra_entry_block = 0;
-
- cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
-
- cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
-
- /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
- BB_START(0);
- for (i = 0; i < op_array->last; i++) {
- zend_op *opline = op_array->opcodes + i;
- switch (opline->opcode) {
- case ZEND_RECV:
- case ZEND_RECV_INIT:
- if (build_flags & ZEND_CFG_RECV_ENTRY) {
- BB_START(i + 1);
- }
- break;
- case ZEND_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_MATCH_ERROR:
- if (i + 1 < op_array->last) {
- BB_START(i + 1);
- }
- break;
- case ZEND_THROW:
- /* Don't treat THROW as terminator if it's used in expression context,
- * as we may lose live ranges when eliminating unreachable code. */
- if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) {
- BB_START(i + 1);
- }
- break;
- case ZEND_INCLUDE_OR_EVAL:
- flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- case ZEND_GENERATOR_CREATE:
- case ZEND_YIELD:
- case ZEND_YIELD_FROM:
- if (build_flags & ZEND_CFG_STACKLESS) {
- BB_START(i + 1);
- }
- break;
- case ZEND_DO_FCALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- flags |= ZEND_FUNC_HAS_CALLS;
- if (build_flags & ZEND_CFG_STACKLESS) {
- BB_START(i + 1);
- }
- break;
- case ZEND_DO_ICALL:
- flags |= ZEND_FUNC_HAS_CALLS;
- break;
- case ZEND_INIT_FCALL:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- zv = CRT_CONSTANT(opline->op2);
- if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
- /* The third literal is the lowercased unqualified name */
- zv += 2;
- }
- if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
- if (fn->type == ZEND_INTERNAL_FUNCTION) {
- flags |= zend_optimizer_classify_function(
- Z_STR_P(zv), opline->extended_value);
- }
- }
- break;
- case ZEND_FAST_CALL:
- BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
- BB_START(i + 1);
- break;
- case ZEND_FAST_RET:
- if (i + 1 < op_array->last) {
- BB_START(i + 1);
- }
- break;
- case ZEND_JMP:
- BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
- if (i + 1 < op_array->last) {
- BB_START(i + 1);
- }
- break;
- case ZEND_JMPZNZ:
- BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
- BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
- if (i + 1 < op_array->last) {
- BB_START(i + 1);
- }
- break;
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
- BB_START(i + 1);
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
- }
- BB_START(i + 1);
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
- BB_START(i + 1);
- break;
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
- BB_START(i + 1);
- break;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- {
- HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
- zval *zv;
- ZEND_HASH_FOREACH_VAL(jumptable, zv) {
- BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
- } ZEND_HASH_FOREACH_END();
- BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
- BB_START(i + 1);
- break;
- }
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- case ZEND_UNSET_VAR:
- case ZEND_ISSET_ISEMPTY_VAR:
- if (opline->extended_value & ZEND_FETCH_LOCAL) {
- flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
- !op_array->function_name) {
- flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- }
- break;
- case ZEND_FUNC_GET_ARGS:
- flags |= ZEND_FUNC_VARARG;
- break;
- case ZEND_EXT_STMT:
- flags |= ZEND_FUNC_HAS_EXTENDED_STMT;
- break;
- case ZEND_EXT_FCALL_BEGIN:
- case ZEND_EXT_FCALL_END:
- flags |= ZEND_FUNC_HAS_EXTENDED_FCALL;
- break;
- case ZEND_FREE:
- if (opline->extended_value == ZEND_FREE_SWITCH) {
- flags |= ZEND_FUNC_FREE_LOOP_VAR;
- }
- break;
- case ZEND_FE_FREE:
- flags |= ZEND_FUNC_FREE_LOOP_VAR;
- break;
- }
- }
-
- /* If the entry block has predecessors, we may need to split it */
- if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
- && op_array->last > 0 && block_map[0] > 1) {
- extra_entry_block = 1;
- }
-
- if (op_array->last_try_catch) {
- for (j = 0; j < op_array->last_try_catch; j++) {
- BB_START(op_array->try_catch_array[j].try_op);
- if (op_array->try_catch_array[j].catch_op) {
- BB_START(op_array->try_catch_array[j].catch_op);
- }
- if (op_array->try_catch_array[j].finally_op) {
- BB_START(op_array->try_catch_array[j].finally_op);
- }
- if (op_array->try_catch_array[j].finally_end) {
- BB_START(op_array->try_catch_array[j].finally_end);
- }
- }
- }
-
- blocks_count += extra_entry_block;
- cfg->blocks_count = blocks_count;
-
- /* Build CFG, Step 2: Build Array of Basic Blocks */
- cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
-
- blocks_count = -1;
-
- if (extra_entry_block) {
- initialize_block(&blocks[0]);
- blocks[0].start = 0;
- blocks[0].len = 0;
- blocks_count++;
- }
-
- for (i = 0; i < op_array->last; i++) {
- if (block_map[i]) {
- if (blocks_count >= 0) {
- blocks[blocks_count].len = i - blocks[blocks_count].start;
- }
- blocks_count++;
- initialize_block(&blocks[blocks_count]);
- blocks[blocks_count].start = i;
- }
- block_map[i] = blocks_count;
- }
-
- blocks[blocks_count].len = i - blocks[blocks_count].start;
- blocks_count++;
-
- /* Build CFG, Step 3: Calculate successors */
- for (j = 0; j < blocks_count; j++) {
- zend_basic_block *block = &blocks[j];
- zend_op *opline;
- if (block->len == 0) {
- block->successors_count = 1;
- block->successors[0] = j + 1;
- continue;
- }
-
- opline = op_array->opcodes + block->start + block->len - 1;
- switch (opline->opcode) {
- case ZEND_FAST_RET:
- case ZEND_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_THROW:
- case ZEND_MATCH_ERROR:
- break;
- case ZEND_JMP:
- block->successors_count = 1;
- block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
- break;
- case ZEND_JMPZNZ:
- block->successors_count = 2;
- block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
- block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
- break;
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- block->successors_count = 2;
- block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
- block->successors[1] = j + 1;
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- block->successors_count = 2;
- block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
- block->successors[1] = j + 1;
- } else {
- block->successors_count = 1;
- block->successors[0] = j + 1;
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- block->successors_count = 2;
- block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
- block->successors[1] = j + 1;
- break;
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- block->successors_count = 2;
- block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
- block->successors[1] = j + 1;
- break;
- case ZEND_FAST_CALL:
- block->successors_count = 2;
- block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
- block->successors[1] = j + 1;
- break;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- {
- HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
- zval *zv;
- uint32_t s = 0;
-
- block->successors_count = (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable);
- block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
-
- ZEND_HASH_FOREACH_VAL(jumptable, zv) {
- block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
- } ZEND_HASH_FOREACH_END();
-
- block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
- if (opline->opcode != ZEND_MATCH) {
- block->successors[s++] = j + 1;
- }
- break;
- }
- default:
- block->successors_count = 1;
- block->successors[0] = j + 1;
- break;
- }
- }
-
- /* Build CFG, Step 4, Mark Reachable Basic Blocks */
- cfg->flags |= flags;
- zend_mark_reachable_blocks(op_array, cfg, 0);
-
- return SUCCESS;
-}
-/* }}} */
-
-int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
-{
- int j, s, edges;
- zend_basic_block *b;
- zend_basic_block *blocks = cfg->blocks;
- zend_basic_block *end = blocks + cfg->blocks_count;
- int *predecessors;
-
- edges = 0;
- for (b = blocks; b < end; b++) {
- b->predecessors_count = 0;
- }
- for (b = blocks; b < end; b++) {
- if (!(b->flags & ZEND_BB_REACHABLE)) {
- b->successors_count = 0;
- b->predecessors_count = 0;
- } else {
- for (s = 0; s < b->successors_count; s++) {
- edges++;
- blocks[b->successors[s]].predecessors_count++;
- }
- }
- }
-
- cfg->edges_count = edges;
- cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges);
-
- edges = 0;
- for (b = blocks; b < end; b++) {
- if (b->flags & ZEND_BB_REACHABLE) {
- b->predecessor_offset = edges;
- edges += b->predecessors_count;
- b->predecessors_count = 0;
- }
- }
-
- for (j = 0; j < cfg->blocks_count; j++) {
- if (blocks[j].flags & ZEND_BB_REACHABLE) {
- /* SWITCH_STRING/LONG may have few identical successors */
- for (s = 0; s < blocks[j].successors_count; s++) {
- int duplicate = 0;
- int p;
-
- for (p = 0; p < s; p++) {
- if (blocks[j].successors[p] == blocks[j].successors[s]) {
- duplicate = 1;
- break;
- }
- }
- if (!duplicate) {
- zend_basic_block *b = blocks + blocks[j].successors[s];
-
- predecessors[b->predecessor_offset + b->predecessors_count] = j;
- b->predecessors_count++;
- }
- }
- }
- }
-
- return SUCCESS;
-}
-/* }}} */
-
-/* Computes a postorder numbering of the CFG */
-static void compute_postnum_recursive(
- int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
-{
- int s;
- zend_basic_block *block = &cfg->blocks[block_num];
- if (postnum[block_num] != -1) {
- return;
- }
-
- postnum[block_num] = -2; /* Marker for "currently visiting" */
- for (s = 0; s < block->successors_count; s++) {
- compute_postnum_recursive(postnum, cur, cfg, block->successors[s]);
- }
- postnum[block_num] = (*cur)++;
-}
-/* }}} */
-
-/* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
- * Cooper, Harvey and Kennedy. */
-int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
-{
- zend_basic_block *blocks = cfg->blocks;
- int blocks_count = cfg->blocks_count;
- int j, k, changed;
-
- ALLOCA_FLAG(use_heap)
- int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap);
- memset(postnum, -1, sizeof(int) * cfg->blocks_count);
- j = 0;
- compute_postnum_recursive(postnum, &j, cfg, 0);
-
- /* FIXME: move declarations */
- blocks[0].idom = 0;
- do {
- changed = 0;
- /* Iterating in RPO here would converge faster */
- for (j = 1; j < blocks_count; j++) {
- int idom = -1;
-
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
- for (k = 0; k < blocks[j].predecessors_count; k++) {
- int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
-
- if (idom < 0) {
- if (blocks[pred].idom >= 0)
- idom = pred;
- continue;
- }
-
- if (blocks[pred].idom >= 0) {
- while (idom != pred) {
- while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom;
- while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom;
- }
- }
- }
-
- if (idom >= 0 && blocks[j].idom != idom) {
- blocks[j].idom = idom;
- changed = 1;
- }
- }
- } while (changed);
- blocks[0].idom = -1;
-
- for (j = 1; j < blocks_count; j++) {
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
- if (blocks[j].idom >= 0) {
- /* Sort by block number to traverse children in pre-order */
- if (blocks[blocks[j].idom].children < 0 ||
- j < blocks[blocks[j].idom].children) {
- blocks[j].next_child = blocks[blocks[j].idom].children;
- blocks[blocks[j].idom].children = j;
- } else {
- int k = blocks[blocks[j].idom].children;
- while (blocks[k].next_child >=0 && j > blocks[k].next_child) {
- k = blocks[k].next_child;
- }
- blocks[j].next_child = blocks[k].next_child;
- blocks[k].next_child = j;
- }
- }
- }
-
- for (j = 0; j < blocks_count; j++) {
- int idom = blocks[j].idom, level = 0;
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
- while (idom >= 0) {
- level++;
- if (blocks[idom].level >= 0) {
- level += blocks[idom].level;
- break;
- } else {
- idom = blocks[idom].idom;
- }
- }
- blocks[j].level = level;
- }
-
- free_alloca(postnum, use_heap);
- return SUCCESS;
-}
-/* }}} */
-
-static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
-{
- while (blocks[b].level > blocks[a].level) {
- b = blocks[b].idom;
- }
- return a == b;
-}
-/* }}} */
-
-typedef struct {
- int id;
- int level;
-} block_info;
-static int compare_block_level(const block_info *a, const block_info *b) {
- return b->level - a->level;
-}
-static void swap_blocks(block_info *a, block_info *b) {
- block_info tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
-{
- int i, j, k, n;
- int time;
- zend_basic_block *blocks = cfg->blocks;
- int *entry_times, *exit_times;
- zend_worklist work;
- int flag = ZEND_FUNC_NO_LOOPS;
- block_info *sorted_blocks;
- ALLOCA_FLAG(list_use_heap)
- ALLOCA_FLAG(tree_use_heap)
- ALLOCA_FLAG(sorted_blocks_use_heap)
-
- ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
-
- /* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
- * queries. These are implemented by checking entry/exit times of the DFS search. */
- entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap);
- exit_times = entry_times + cfg->blocks_count;
- memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count);
-
- zend_worklist_push(&work, 0);
- time = 0;
- while (zend_worklist_len(&work)) {
- next:
- i = zend_worklist_peek(&work);
- if (entry_times[i] == -1) {
- entry_times[i] = time++;
- }
- /* Visit blocks immediately dominated by i. */
- for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
- if (zend_worklist_push(&work, j)) {
- goto next;
- }
- }
- /* Visit join edges. */
- for (j = 0; j < blocks[i].successors_count; j++) {
- int succ = blocks[i].successors[j];
- if (blocks[succ].idom == i) {
- continue;
- } else if (zend_worklist_push(&work, succ)) {
- goto next;
- }
- }
- exit_times[i] = time++;
- zend_worklist_pop(&work);
- }
-
- /* Sort blocks by decreasing level, which is the order in which we want to process them */
- sorted_blocks = do_alloca(sizeof(block_info) * cfg->blocks_count, sorted_blocks_use_heap);
- for (i = 0; i < cfg->blocks_count; i++) {
- sorted_blocks[i].id = i;
- sorted_blocks[i].level = blocks[i].level;
- }
- zend_sort(sorted_blocks, cfg->blocks_count, sizeof(block_info),
- (compare_func_t) compare_block_level, (swap_func_t) swap_blocks);
-
- /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
- Graphs". */
-
- for (n = 0; n < cfg->blocks_count; n++) {
- i = sorted_blocks[n].id;
-
- zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count));
- for (j = 0; j < blocks[i].predecessors_count; j++) {
- int pred = cfg->predecessors[blocks[i].predecessor_offset + j];
-
- /* A join edge is one for which the predecessor does not
- immediately dominate the successor. */
- if (blocks[i].idom == pred) {
- continue;
- }
-
- /* In a loop back-edge (back-join edge), the successor dominates
- the predecessor. */
- if (dominates(blocks, i, pred)) {
- blocks[i].flags |= ZEND_BB_LOOP_HEADER;
- flag &= ~ZEND_FUNC_NO_LOOPS;
- zend_worklist_push(&work, pred);
- } else {
- /* Otherwise it's a cross-join edge. See if it's a branch
- to an ancestor on the DJ spanning tree. */
- if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) {
- blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
- flag |= ZEND_FUNC_IRREDUCIBLE;
- flag &= ~ZEND_FUNC_NO_LOOPS;
- }
- }
- }
- while (zend_worklist_len(&work)) {
- j = zend_worklist_pop(&work);
- while (blocks[j].loop_header >= 0) {
- j = blocks[j].loop_header;
- }
- if (j != i) {
- blocks[j].loop_header = i;
- for (k = 0; k < blocks[j].predecessors_count; k++) {
- zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
- }
- }
- }
- }
-
- free_alloca(sorted_blocks, sorted_blocks_use_heap);
- free_alloca(entry_times, tree_use_heap);
- ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
-
- cfg->flags |= flag;
-
- return SUCCESS;
-}
-/* }}} */
diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h
deleted file mode 100644
index eb607c83c2..0000000000
--- a/ext/opcache/Optimizer/zend_cfg.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, CFG - Control Flow Graph |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef ZEND_CFG_H
-#define ZEND_CFG_H
-
-/* zend_basic_block.flags */
-#define ZEND_BB_START (1<<0) /* first block */
-#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
-#define ZEND_BB_TARGET (1<<2) /* jump target */
-#define ZEND_BB_EXIT (1<<3) /* without successors */
-#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
-#define ZEND_BB_TRY (1<<5) /* start of try block */
-#define ZEND_BB_CATCH (1<<6) /* start of catch block */
-#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
-#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
-#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
-#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
-
-#define ZEND_BB_LOOP_HEADER (1<<16)
-#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
-
-#define ZEND_BB_REACHABLE (1U<<31)
-
-#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE)
-
-typedef struct _zend_basic_block {
- int *successors; /* successor block indices */
- uint32_t flags;
- uint32_t start; /* first opcode number */
- uint32_t len; /* number of opcodes */
- int successors_count; /* number of successors */
- int predecessors_count; /* number of predecessors */
- int predecessor_offset; /* offset of 1-st predecessor */
- int idom; /* immediate dominator block */
- int loop_header; /* closest loop header, or -1 */
- int level; /* steps away from the entry in the dom. tree */
- int children; /* list of dominated blocks */
- int next_child; /* next dominated block */
- int successors_storage[2]; /* up to 2 successor blocks */
-} zend_basic_block;
-
-/*
-+------------+---+---+---+---+---+
-| |OP1|OP2|EXT| 0 | 1 |
-+------------+---+---+---+---+---+
-|JMP |ADR| | |OP1| - |
-|JMPZ | |ADR| |OP2|FOL|
-|JMPNZ | |ADR| |OP2|FOL|
-|JMPZNZ | |ADR|ADR|OP2|EXT|
-|JMPZ_EX | |ADR| |OP2|FOL|
-|JMPNZ_EX | |ADR| |OP2|FOL|
-|JMP_SET | |ADR| |OP2|FOL|
-|COALESCE | |ADR| |OP2|FOL|
-|ASSERT_CHK | |ADR| |OP2|FOL|
-|NEW | |ADR| |OP2|FOL|
-|DCL_ANON* |ADR| | |OP1|FOL|
-|FE_RESET_* | |ADR| |OP2|FOL|
-|FE_FETCH_* | | |ADR|EXT|FOL|
-|CATCH | | |ADR|EXT|FOL|
-|FAST_CALL |ADR| | |OP1|FOL|
-|FAST_RET | | | | - | - |
-|RETURN* | | | | - | - |
-|EXIT | | | | - | - |
-|THROW | | | | - | - |
-|* | | | |FOL| - |
-+------------+---+---+---+---+---+
-*/
-
-typedef struct _zend_cfg {
- int blocks_count; /* number of basic blocks */
- int edges_count; /* number of edges */
- zend_basic_block *blocks; /* array of basic blocks */
- int *predecessors;
- uint32_t *map;
- uint32_t flags;
-} zend_cfg;
-
-/* Build Flags */
-#define ZEND_CFG_STACKLESS (1<<30)
-#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
-#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
-#define ZEND_SSA_RC_INFERENCE (1<<27)
-#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
-#define ZEND_CFG_RECV_ENTRY (1<<24)
-#define ZEND_CALL_TREE (1<<23)
-#define ZEND_SSA_USE_CV_RESULTS (1<<22)
-
-#define CRT_CONSTANT_EX(op_array, opline, node) \
- (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \
- RT_CONSTANT(opline, (node)) \
- : \
- CT_CONSTANT_EX(op_array, (node).constant) \
- )
-
-#define CRT_CONSTANT(node) \
- CRT_CONSTANT_EX(op_array, opline, node)
-
-#define RETURN_VALUE_USED(opline) \
- ((opline)->result_type != IS_UNUSED)
-
-BEGIN_EXTERN_C()
-
-int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
-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(const zend_op_array *op_array, zend_cfg *cfg);
-int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg);
-
-END_EXTERN_C()
-
-#endif /* ZEND_CFG_H */
diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c
deleted file mode 100644
index 25a910ef71..0000000000
--- a/ext/opcache/Optimizer/zend_dfg.c
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, DFG - Data Flow Graph |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "zend_compile.h"
-#include "zend_dfg.h"
-
-static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
-{
- uint32_t var_num;
- const zend_op *next;
-
- if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- var_num = EX_VAR_TO_NUM(opline->op1.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- }
- if (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0
- && opline->opcode != ZEND_FE_FETCH_R
- && opline->opcode != ZEND_FE_FETCH_RW)
- || (opline->op2_type == IS_CV)) {
- var_num = EX_VAR_TO_NUM(opline->op2.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- }
- if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
- && opline->result_type == IS_CV
- && opline->opcode != ZEND_RECV) {
- var_num = EX_VAR_TO_NUM(opline->result.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- }
-
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
- zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
- }
- if (opline->op1_type == IS_CV) {
-add_op1_def:
- zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op1.var));
- }
- break;
- case ZEND_ASSIGN_REF:
- if (opline->op2_type == IS_CV) {
- zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- var_num = EX_VAR_TO_NUM(next->op1.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
- zend_bitset_incl(def, var_num);
- }
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_OBJ_REF:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- var_num = EX_VAR_TO_NUM(next->op1.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- if (next->op1_type == IS_CV) {
- zend_bitset_incl(def, var_num);
- }
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- var_num = EX_VAR_TO_NUM(next->op1.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
- zend_bitset_incl(def, var_num);
- }
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP_REF:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- var_num = EX_VAR_TO_NUM(next->op1.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- if (next->op1_type == IS_CV) {
- zend_bitset_incl(def, var_num);
- }
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP_OP:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- var_num = EX_VAR_TO_NUM(next->op1.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- }
- break;
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- var_num = EX_VAR_TO_NUM(next->op1.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_OP:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- case ZEND_BIND_GLOBAL:
- case ZEND_BIND_STATIC:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_REF:
- case ZEND_SEND_UNPACK:
- case ZEND_FE_RESET_RW:
- case ZEND_MAKE_REF:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- case ZEND_UNSET_DIM:
- case ZEND_UNSET_OBJ:
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_W:
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_SEND_VAR:
- case ZEND_CAST:
- case ZEND_QM_ASSIGN:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_FE_RESET_R:
- if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ADD_ARRAY_UNPACK:
- var_num = EX_VAR_TO_NUM(opline->result.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- break;
- case ZEND_ADD_ARRAY_ELEMENT:
- var_num = EX_VAR_TO_NUM(opline->result.var);
- if (!zend_bitset_in(def, var_num)) {
- zend_bitset_incl(use, var_num);
- }
- /* break missing intentionally */
- case ZEND_INIT_ARRAY:
- if (((build_flags & ZEND_SSA_RC_INFERENCE)
- || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
- && opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_YIELD:
- if (opline->op1_type == IS_CV
- && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
- || (build_flags & ZEND_SSA_RC_INFERENCE))) {
- goto add_op1_def;
- }
- break;
- case ZEND_UNSET_CV:
- goto add_op1_def;
- case ZEND_VERIFY_RETURN_TYPE:
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
- goto add_op1_def;
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
-#if 0
- /* This special case was handled above the switch */
- if (opline->op2_type != IS_CV) {
- op2_use = -1; /* not used */
- }
-#endif
- zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
- break;
- case ZEND_BIND_LEXICAL:
- if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
- zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
- }
- break;
- default:
- break;
- }
-
- if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- zend_bitset_incl(def, EX_VAR_TO_NUM(opline->result.var));
- }
-}
-/* }}} */
-
-void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
-{
- _zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
-}
-/* }}} */
-
-int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
-{
- int set_size;
- zend_basic_block *blocks = cfg->blocks;
- int blocks_count = cfg->blocks_count;
- zend_bitset tmp, def, use, in, out;
- int k;
- int j;
-
- set_size = dfg->size;
- tmp = dfg->tmp;
- def = dfg->def;
- use = dfg->use;
- in = dfg->in;
- out = dfg->out;
-
- /* Collect "def" and "use" sets */
- for (j = 0; j < blocks_count; j++) {
- zend_op *opline, *end;
- zend_bitset b_use, b_def;
-
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
-
- opline = op_array->opcodes + blocks[j].start;
- end = opline + blocks[j].len;
- b_use = DFG_BITSET(use, set_size, j);
- b_def = DFG_BITSET(def, set_size, j);
- for (; opline < end; opline++) {
- if (opline->opcode != ZEND_OP_DATA) {
- _zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def);
- }
- }
- }
-
- /* Calculate "in" and "out" sets */
- {
- uint32_t worklist_len = zend_bitset_len(blocks_count);
- zend_bitset worklist;
- ALLOCA_FLAG(use_heap);
- worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
- memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
- for (j = 0; j < blocks_count; j++) {
- zend_bitset_incl(worklist, j);
- }
- while (!zend_bitset_empty(worklist, worklist_len)) {
- /* We use the last block on the worklist, because predecessors tend to be located
- * before the succeeding block, so this converges faster. */
- j = zend_bitset_last(worklist, worklist_len);
- zend_bitset_excl(worklist, j);
-
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
- if (blocks[j].successors_count != 0) {
- zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
- for (k = 1; k < blocks[j].successors_count; k++) {
- zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size);
- }
- } else {
- zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
- }
- zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
- if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
- zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);
-
- /* Add predecessors of changed block to worklist */
- {
- int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
- for (k = 0; k < blocks[j].predecessors_count; k++) {
- zend_bitset_incl(worklist, predecessors[k]);
- }
- }
- }
- }
-
- free_alloca(worklist, use_heap);
- }
-
- return SUCCESS;
-}
-/* }}} */
diff --git a/ext/opcache/Optimizer/zend_dfg.h b/ext/opcache/Optimizer/zend_dfg.h
deleted file mode 100644
index a675187794..0000000000
--- a/ext/opcache/Optimizer/zend_dfg.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, DFG - Data Flow Graph |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef ZEND_DFG_H
-#define ZEND_DFG_H
-
-#include "zend_bitset.h"
-#include "zend_cfg.h"
-
-typedef struct _zend_dfg {
- int vars;
- uint32_t size;
- zend_bitset tmp;
- zend_bitset def;
- zend_bitset use;
- zend_bitset in;
- zend_bitset out;
-} zend_dfg;
-
-#define DFG_BITSET(set, set_size, block_num) \
- ((set) + ((block_num) * (set_size)))
-
-#define DFG_SET(set, set_size, block_num, var_num) \
- zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num))
-
-#define DFG_ISSET(set, set_size, block_num, var_num) \
- zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num))
-
-BEGIN_EXTERN_C()
-
-int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
-void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def);
-
-END_EXTERN_C()
-
-#endif /* ZEND_DFG_H */
diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c
deleted file mode 100644
index cb835574d8..0000000000
--- a/ext/opcache/Optimizer/zend_dump.c
+++ /dev/null
@@ -1,1239 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Bytecode Visualisation |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#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"
-
-void zend_dump_ht(HashTable *ht)
-{
- zend_ulong index;
- zend_string *key;
- zval *val;
- int first = 1;
-
- ZEND_HASH_FOREACH_KEY_VAL(ht, index, key, val) {
- if (first) {
- first = 0;
- } else {
- fprintf(stderr, ", ");
- }
- if (key) {
- fprintf(stderr, "\"%s\"", ZSTR_VAL(key));
- } else {
- fprintf(stderr, ZEND_LONG_FMT, index);
- }
- fprintf(stderr, " =>");
- zend_dump_const(val);
- } ZEND_HASH_FOREACH_END();
-}
-
-void zend_dump_const(const zval *zv)
-{
- switch (Z_TYPE_P(zv)) {
- case IS_NULL:
- fprintf(stderr, " null");
- break;
- case IS_FALSE:
- fprintf(stderr, " bool(false)");
- break;
- case IS_TRUE:
- fprintf(stderr, " bool(true)");
- break;
- case IS_LONG:
- fprintf(stderr, " int(" ZEND_LONG_FMT ")", Z_LVAL_P(zv));
- break;
- case IS_DOUBLE:
- fprintf(stderr, " float(%g)", Z_DVAL_P(zv));
- break;
- case IS_STRING:
- fprintf(stderr, " string(\"%s\")", Z_STRVAL_P(zv));
- break;
- case IS_ARRAY:
- fprintf(stderr, " array(...)");
- break;
- default:
- fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
- break;
- }
-}
-
-static void zend_dump_class_fetch_type(uint32_t fetch_type)
-{
- switch (fetch_type & ZEND_FETCH_CLASS_MASK) {
- case ZEND_FETCH_CLASS_SELF:
- fprintf(stderr, " (self)");
- break;
- case ZEND_FETCH_CLASS_PARENT:
- fprintf(stderr, " (parent)");
- break;
- case ZEND_FETCH_CLASS_STATIC:
- fprintf(stderr, " (static)");
- break;
- case ZEND_FETCH_CLASS_AUTO:
- fprintf(stderr, " (auto)");
- break;
- case ZEND_FETCH_CLASS_INTERFACE:
- fprintf(stderr, " (interface)");
- break;
- case ZEND_FETCH_CLASS_TRAIT:
- fprintf(stderr, " (trait)");
- break;
- }
- if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) {
- fprintf(stderr, " (no-autolod)");
- }
- if (fetch_type & ZEND_FETCH_CLASS_SILENT) {
- fprintf(stderr, " (silent)");
- }
- if (fetch_type & ZEND_FETCH_CLASS_EXCEPTION) {
- fprintf(stderr, " (exception)");
- }
-}
-
-static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t flags) {
- if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
- fprintf(stderr, " %u", op.num);
- } else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
- if (op.num != (uint32_t)-1) {
- fprintf(stderr, " try-catch(%u)", op.num);
- }
- } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
- fprintf(stderr, " THIS");
- } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {
- fprintf(stderr, " NEXT");
- } else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) {
- zend_dump_class_fetch_type(op.num);
- } else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) {
- fprintf(stderr, " CONSTRUCTOR");
- } else if (ZEND_VM_OP_CONST_FETCH == (flags & ZEND_VM_OP_MASK)) {
- if (op.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
- fprintf(stderr, " (unqualified-in-namespace)");
- }
- }
-}
-
-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_VAR|IS_TMP_VAR)) == 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 if (r->min == ZEND_LONG_MIN) {
- fprintf(stderr, "MIN..");
- } else {
- fprintf(stderr, ZEND_LONG_FMT "..", r->min);
- }
- if (r->overflow) {
- fprintf(stderr, "++]");
- } else if (r->max == ZEND_LONG_MAX) {
- fprintf(stderr, "MAX]");
- } else {
- fprintf(stderr, ZEND_LONG_FMT "]", r->max);
- }
-}
-
-static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof, uint32_t dump_flags)
-{
- int first = 1;
-
- fprintf(stderr, " [");
- if (info & MAY_BE_GUARD) {
- fprintf(stderr, "!");
- }
- if (info & MAY_BE_UNDEF) {
- if (first) first = 0; else fprintf(stderr, ", ");
- fprintf(stderr, "undef");
- }
- if (info & MAY_BE_REF) {
- if (first) first = 0; else fprintf(stderr, ", ");
- fprintf(stderr, "ref");
- }
- if (dump_flags & ZEND_DUMP_RC_INFERENCE) {
- 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) && (info & MAY_BE_TRUE)) {
- if (first) first = 0; else fprintf(stderr, ", ");
- fprintf(stderr, "bool");
- } else if (info & MAY_BE_FALSE) {
- if (first) first = 0; else fprintf(stderr, ", ");
- fprintf(stderr, "false");
- } else 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_LONG) == 0 ||
- (info & MAY_BE_ARRAY_KEY_STRING) == 0)) {
- 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");
- }
- }
- fprintf(stderr, "]");
-}
-
-static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num, uint32_t dump_flags)
-{
- 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,
- dump_flags);
-}
-
-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, uint32_t dump_flags)
-{
- 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->vars[ssa_var_num].no_val) {
- fprintf(stderr, " NOVAL");
- }
- if (ssa->vars[ssa_var_num].escape_state == ESCAPE_STATE_NO_ESCAPE) {
- fprintf(stderr, " NOESC");
- }
- if (ssa->var_info) {
- zend_dump_ssa_var_info(ssa, ssa_var_num, dump_flags);
- if (ssa->var_info[ssa_var_num].has_range) {
- zend_dump_range(&ssa->var_info[ssa_var_num].range);
- }
- }
- }
-}
-
-static void zend_dump_type_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_type_constraint *constraint, uint32_t dump_flags)
-{
- fprintf(stderr, " TYPE");
- zend_dump_type_info(constraint->type_mask, constraint->ce, 1, dump_flags);
-}
-
-static void zend_dump_range_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_range_constraint *r, uint32_t dump_flags)
-{
- 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, dump_flags);
- 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, dump_flags);
- 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);
- }
- }
-}
-
-void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op)
-{
- const char *name = zend_get_opcode_name(opline->opcode);
- uint32_t flags = zend_get_opcode_flags(opline->opcode);
- uint32_t n = 0;
-
- if (!ssa_op || ssa_op->result_use < 0) {
- if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- if (ssa_op && ssa_op->result_def >= 0) {
- int ssa_var_num = ssa_op->result_def;
- zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
- } else {
- zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
- }
- fprintf(stderr, " = ");
- }
- }
-
- 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_OP == (flags & ZEND_VM_EXT_MASK)) {
- fprintf(stderr, " (%s)", zend_get_opcode_name(opline->extended_value) + 5);
- } else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) {
- switch (opline->extended_value) {
- case IS_NULL:
- fprintf(stderr, " (null)");
- break;
- case IS_FALSE:
- fprintf(stderr, " (false)");
- break;
- case IS_TRUE:
- fprintf(stderr, " (true)");
- break;
- case IS_LONG:
- fprintf(stderr, " (long)");
- break;
- case IS_DOUBLE:
- fprintf(stderr, " (double)");
- break;
- case IS_STRING:
- fprintf(stderr, " (string)");
- break;
- case IS_ARRAY:
- fprintf(stderr, " (array)");
- break;
- case IS_OBJECT:
- fprintf(stderr, " (object)");
- break;
- case IS_RESOURCE:
- fprintf(stderr, " (resource)");
- break;
- case _IS_BOOL:
- fprintf(stderr, " (bool)");
- break;
- case IS_CALLABLE:
- fprintf(stderr, " (callable)");
- break;
- case IS_VOID:
- fprintf(stderr, " (void)");
- break;
- default:
- fprintf(stderr, " (\?\?\?)");
- break;
- }
- } else if (ZEND_VM_EXT_TYPE_MASK == (flags & ZEND_VM_EXT_MASK)) {
- switch (opline->extended_value) {
- case (1<<IS_NULL):
- fprintf(stderr, " (null)");
- break;
- case (1<<IS_FALSE):
- fprintf(stderr, " (false)");
- break;
- case (1<<IS_TRUE):
- fprintf(stderr, " (true)");
- break;
- case (1<<IS_LONG):
- fprintf(stderr, " (long)");
- break;
- case (1<<IS_DOUBLE):
- fprintf(stderr, " (double)");
- break;
- case (1<<IS_STRING):
- fprintf(stderr, " (string)");
- break;
- case (1<<IS_ARRAY):
- fprintf(stderr, " (array)");
- break;
- case (1<<IS_OBJECT):
- fprintf(stderr, " (object)");
- break;
- case (1<<IS_RESOURCE):
- fprintf(stderr, " (resource)");
- break;
- case ((1<<IS_FALSE)|(1<<IS_TRUE)):
- fprintf(stderr, " (bool)");
- break;
- default:
- fprintf(stderr, " TYPE");
- zend_dump_type_info(opline->extended_value, NULL, 0, dump_flags);
- break;
- }
- } else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) {
- switch (opline->extended_value) {
- case ZEND_EVAL:
- fprintf(stderr, " (eval)");
- break;
- case ZEND_INCLUDE:
- fprintf(stderr, " (include)");
- break;
- case ZEND_INCLUDE_ONCE:
- fprintf(stderr, " (include_once)");
- break;
- case ZEND_REQUIRE:
- fprintf(stderr, " (require)");
- break;
- case ZEND_REQUIRE_ONCE:
- fprintf(stderr, " (require_once)");
- break;
- default:
- fprintf(stderr, " (\?\?\?)");
- break;
- }
- } else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) {
- if (opline->extended_value == ZEND_RETURNS_VALUE) {
- fprintf(stderr, " (value)");
- } else if (opline->extended_value & ZEND_RETURNS_FUNCTION) {
- fprintf(stderr, " (function)");
- }
- } else {
- if (ZEND_VM_EXT_VAR_FETCH & flags) {
- if (opline->extended_value & ZEND_FETCH_GLOBAL) {
- fprintf(stderr, " (global)");
- } else if (opline->extended_value & ZEND_FETCH_LOCAL) {
- fprintf(stderr, " (local)");
- } else if (opline->extended_value & ZEND_FETCH_GLOBAL_LOCK) {
- fprintf(stderr, " (global+lock)");
- }
- }
- if (ZEND_VM_EXT_ISSET & flags) {
- if (!(opline->extended_value & ZEND_ISEMPTY)) {
- fprintf(stderr, " (isset)");
- } else {
- fprintf(stderr, " (empty)");
- }
- }
- if (ZEND_VM_EXT_ARRAY_INIT & flags) {
- fprintf(stderr, " %u", opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT);
- if (!(opline->extended_value & ZEND_ARRAY_NOT_PACKED)) {
- fprintf(stderr, " (packed)");
- }
- }
- if (ZEND_VM_EXT_REF & flags) {
- if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
- fprintf(stderr, " (ref)");
- }
- }
- if ((ZEND_VM_EXT_DIM_WRITE|ZEND_VM_EXT_FETCH_REF) & flags) {
- uint32_t obj_flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
- if (obj_flags == ZEND_FETCH_REF) {
- fprintf(stderr, " (ref)");
- } else if (obj_flags == ZEND_FETCH_DIM_WRITE) {
- fprintf(stderr, " (dim write)");
- }
- }
- }
-
- if (opline->op1_type == IS_CONST) {
- zend_dump_const(CRT_CONSTANT(opline->op1));
- } else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- if (ssa_op) {
- int ssa_var_num = ssa_op->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), dump_flags);
- } else if (ssa_op->op1_def < 0) {
- fprintf(stderr, " ");
- zend_dump_var(op_array, 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_op) {
- int ssa_var_num = ssa_op->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), dump_flags);
- }
- }
- } else {
- uint32_t op1_flags = ZEND_VM_OP1_FLAGS(flags);
- if (ZEND_VM_OP_JMP_ADDR == (op1_flags & ZEND_VM_OP_MASK)) {
- if (b) {
- fprintf(stderr, " BB%d", b->successors[n++]);
- } else {
- fprintf(stderr, " %04u", (uint32_t)(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes));
- }
- } else {
- zend_dump_unused_op(opline, opline->op1, op1_flags);
- }
- }
-
- if (opline->op2_type == IS_CONST) {
- zval *op = CRT_CONSTANT(opline->op2);
- if (
- opline->opcode == ZEND_SWITCH_LONG
- || opline->opcode == ZEND_SWITCH_STRING
- || opline->opcode == ZEND_MATCH
- ) {
- HashTable *jumptable = Z_ARRVAL_P(op);
- zend_string *key;
- zend_ulong num_key;
- zval *zv;
- ZEND_HASH_FOREACH_KEY_VAL(jumptable, num_key, key, zv) {
- if (key) {
- fprintf(stderr, " \"%s\":", ZSTR_VAL(key));
- } else {
- fprintf(stderr, " " ZEND_LONG_FMT ":", num_key);
- }
- if (b) {
- fprintf(stderr, " BB%d,", b->successors[n++]);
- } else {
- fprintf(stderr, " %04u,", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
- }
- } ZEND_HASH_FOREACH_END();
- fprintf(stderr, " default:");
- } else {
- zend_dump_const(op);
- }
- } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- if (ssa_op) {
- int ssa_var_num = ssa_op->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), dump_flags);
- } else if (ssa_op->op2_def < 0) {
- fprintf(stderr, " ");
- zend_dump_var(op_array, 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_op) {
- int ssa_var_num = ssa_op->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), dump_flags);
- }
- }
- } else {
- uint32_t op2_flags = ZEND_VM_OP2_FLAGS(flags);
- if (ZEND_VM_OP_JMP_ADDR == (op2_flags & ZEND_VM_OP_MASK)) {
- if (opline->opcode != ZEND_CATCH || !(opline->extended_value & ZEND_LAST_CATCH)) {
- if (b) {
- fprintf(stderr, " BB%d", b->successors[n++]);
- } else {
- fprintf(stderr, " %04u", (uint32_t)(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes));
- }
- }
- } else {
- zend_dump_unused_op(opline, opline->op2, op2_flags);
- }
- }
-
- if (ZEND_VM_EXT_JMP_ADDR == (flags & ZEND_VM_EXT_MASK)) {
- if (b) {
- fprintf(stderr, " BB%d", b->successors[n++]);
- } else {
- fprintf(stderr, " %04u", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
- }
- }
- if (opline->result_type == IS_CONST) {
- zend_dump_const(CRT_CONSTANT(opline->result));
-#if 0
- } else if (opline->result_type & IS_SMART_BRANCH_JMPZ) {
- fprintf(stderr, " jmpz");
- } else if (opline->result_type & IS_SMART_BRANCH_JMPNZ) {
- fprintf(stderr, " jmpnz");
-#endif
- } else if (ssa_op && ssa_op->result_use >= 0) {
- if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- if (ssa_op) {
- int ssa_var_num = ssa_op->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), dump_flags);
- }
- } else {
- fprintf(stderr, " ");
- zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
- }
- if (ssa_op) {
- int ssa_var_num = ssa_op->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), dump_flags);
- }
- }
- }
- }
-}
-
-static void zend_dump_op_line(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
-{
- int len = 0;
- const zend_ssa *ssa = NULL;
- zend_ssa_op *ssa_op = NULL;
-
- len = fprintf(stderr, "%04u", (uint32_t)(opline - op_array->opcodes));
- fprintf(stderr, "%*c", 5-len, ' ');
-
- if (dump_flags & ZEND_DUMP_SSA) {
- ssa = (const zend_ssa*)data;
- if (ssa && ssa->ops) {
- ssa_op = &ssa->ops[opline - op_array->opcodes];
- }
- }
-
- zend_dump_op(op_array, b, opline, dump_flags, ssa, ssa_op);
- fprintf(stderr, "\n");
-}
-
-static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
-{
- zend_basic_block *b = cfg->blocks + n;
-
- if (n > 0) {
- fprintf(stderr, "\n");
- }
- fprintf(stderr, "BB%d:\n ;", n);
- if (b->flags & ZEND_BB_START) {
- fprintf(stderr, " start");
- }
- if (b->flags & ZEND_BB_RECV_ENTRY) {
- fprintf(stderr, " recv");
- }
- if (b->flags & ZEND_BB_FOLLOW) {
- fprintf(stderr, " follow");
- }
- if (b->flags & ZEND_BB_TARGET) {
- fprintf(stderr, " target");
- }
- if (b->flags & ZEND_BB_EXIT) {
- fprintf(stderr, " exit");
- }
- if (b->flags & (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY)) {
- fprintf(stderr, " entry");
- }
- if (b->flags & ZEND_BB_TRY) {
- fprintf(stderr, " try");
- }
- if (b->flags & ZEND_BB_CATCH) {
- fprintf(stderr, " catch");
- }
- if (b->flags & ZEND_BB_FINALLY) {
- fprintf(stderr, " finally");
- }
- if (b->flags & ZEND_BB_FINALLY_END) {
- fprintf(stderr, " finally_end");
- }
- if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) && !(b->flags & ZEND_BB_REACHABLE)) {
- fprintf(stderr, " unreachable");
- }
- if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
- fprintf(stderr, " unreachable_free");
- }
- if (b->flags & ZEND_BB_LOOP_HEADER) {
- fprintf(stderr, " loop_header");
- }
- if (b->flags & ZEND_BB_IRREDUCIBLE_LOOP) {
- fprintf(stderr, " irreducible");
- }
- if (b->len != 0) {
- fprintf(stderr, " lines=[%d-%d]", b->start, b->start + b->len - 1);
- } else {
- fprintf(stderr, " empty");
- }
- fprintf(stderr, "\n");
-
- if (b->predecessors_count) {
- int *p = cfg->predecessors + b->predecessor_offset;
- int *end = p + b->predecessors_count;
-
- fprintf(stderr, " ; from=(BB%d", *p);
- for (p++; p < end; p++) {
- fprintf(stderr, ", BB%d", *p);
- }
- fprintf(stderr, ")\n");
- }
-
- if (b->successors_count > 0) {
- int s;
- fprintf(stderr, " ; to=(BB%d", b->successors[0]);
- for (s = 1; s < b->successors_count; s++) {
- fprintf(stderr, ", BB%d", b->successors[s]);
- }
- fprintf(stderr, ")\n");
- }
-
- if (b->idom >= 0) {
- fprintf(stderr, " ; idom=BB%d\n", b->idom);
- }
- if (b->level >= 0) {
- fprintf(stderr, " ; level=%d\n", b->level);
- }
- if (b->loop_header >= 0) {
- fprintf(stderr, " ; loop_header=%d\n", b->loop_header);
- }
- if (b->children >= 0) {
- int j = b->children;
- fprintf(stderr, " ; children=(BB%d", j);
- j = cfg->blocks[j].next_child;
- while (j >= 0) {
- fprintf(stderr, ", BB%d", j);
- j = cfg->blocks[j].next_child;
- }
- fprintf(stderr, ")\n");
- }
-}
-
-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)
-{
- 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, dump_flags);
- 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, dump_flags);
- }
- fprintf(stderr, ")\n");
- } else {
- fprintf(stderr, " = Pi<BB%d>(", p->pi);
- zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var, dump_flags);
- fprintf(stderr, " &");
- if (p->has_range_constraint) {
- zend_dump_range_constraint(op_array, ssa, &p->constraint.range, dump_flags);
- } else {
- zend_dump_type_constraint(op_array, ssa, &p->constraint.type, dump_flags);
- }
- fprintf(stderr, ")\n");
- }
- p = p->next;
- } while (p);
- }
-}
-
-void zend_dump_op_array_name(const zend_op_array *op_array)
-{
- if (op_array->function_name) {
- if (op_array->scope && op_array->scope->name) {
- fprintf(stderr, "%s::%s", op_array->scope->name->val, op_array->function_name->val);
- } else {
- fprintf(stderr, "%s", op_array->function_name->val);
- }
- } else {
- fprintf(stderr, "%s", "$_main");
- }
-}
-
-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 (!cfg->blocks) {
- cfg = data = NULL;
- }
- }
- if (dump_flags & ZEND_DUMP_SSA) {
- ssa = (const zend_ssa*)data;
- }
-
- func_info = ZEND_FUNC_INFO(op_array);
- if (func_info) {
- func_flags = func_info->flags;
- }
-
- fprintf(stderr, "\n");
- zend_dump_op_array_name(op_array);
- fprintf(stderr, ":\n ; (lines=%d, args=%d",
- op_array->last,
- op_array->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_INDIRECT_VAR_ACCESS) {
- fprintf(stderr, ", dynamic");
- }
- 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");
- }
- if (func_flags & ZEND_FUNC_HAS_EXTENDED_STMT) {
- fprintf(stderr, ", extended_stmt");
- }
- if (func_flags & ZEND_FUNC_HAS_EXTENDED_FCALL) {
- fprintf(stderr, ", extended_fcall");
- }
-//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
- 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) {
- fprintf(stderr, " ; return ");
- zend_dump_type_info(func_info->return_info.type, func_info->return_info.ce, func_info->return_info.is_instanceof, dump_flags);
- 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, dump_flags);
- 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_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
- const zend_op *opline;
- const zend_op *end;
-
- zend_dump_block_header(cfg, op_array, ssa, n, dump_flags);
- opline = op_array->opcodes + b->start;
- end = opline + b->len;
- while (opline < end) {
- zend_dump_op_line(op_array, b, opline, dump_flags, data);
- opline++;
- }
- }
- }
- if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) {
- fprintf(stderr, "LIVE RANGES:\n");
- for (i = 0; i < op_array->last_live_range; i++) {
- fprintf(stderr,
- " %u: %04u - %04u ",
- EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
- op_array->live_range[i].start,
- op_array->live_range[i].end);
- switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
- case ZEND_LIVE_TMPVAR:
- fprintf(stderr, "(tmp/var)\n");
- break;
- case ZEND_LIVE_LOOP:
- fprintf(stderr, "(loop)\n");
- break;
- case ZEND_LIVE_SILENCE:
- fprintf(stderr, "(silence)\n");
- break;
- case ZEND_LIVE_ROPE:
- fprintf(stderr, "(rope)\n");
- break;
- case ZEND_LIVE_NEW:
- fprintf(stderr, "(new)\n");
- break;
- }
- }
- }
- if (op_array->last_try_catch) {
- fprintf(stderr, "EXCEPTION TABLE:\n");
- for (i = 0; i < op_array->last_try_catch; i++) {
- fprintf(stderr, " BB%u",
- cfg->map[op_array->try_catch_array[i].try_op]);
- if (op_array->try_catch_array[i].catch_op) {
- fprintf(stderr, ", BB%u",
- cfg->map[op_array->try_catch_array[i].catch_op]);
- } else {
- fprintf(stderr, ", -");
- }
- if (op_array->try_catch_array[i].finally_op) {
- fprintf(stderr, ", BB%u",
- cfg->map[op_array->try_catch_array[i].finally_op]);
- } else {
- fprintf(stderr, ", -");
- }
- if (op_array->try_catch_array[i].finally_end) {
- fprintf(stderr, ", BB%u\n",
- cfg->map[op_array->try_catch_array[i].finally_end]);
- } else {
- fprintf(stderr, ", -\n");
- }
- }
- }
- } else {
- const zend_op *opline = op_array->opcodes;
- const zend_op *end = opline + op_array->last;
-
- while (opline < end) {
- zend_dump_op_line(op_array, NULL, opline, dump_flags, data);
- opline++;
- }
- if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) {
- fprintf(stderr, "LIVE RANGES:\n");
- for (i = 0; i < op_array->last_live_range; i++) {
- fprintf(stderr,
- " %u: %04u - %04u ",
- EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
- op_array->live_range[i].start,
- op_array->live_range[i].end);
- switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
- case ZEND_LIVE_TMPVAR:
- fprintf(stderr, "(tmp/var)\n");
- break;
- case ZEND_LIVE_LOOP:
- fprintf(stderr, "(loop)\n");
- break;
- case ZEND_LIVE_SILENCE:
- fprintf(stderr, "(silence)\n");
- break;
- case ZEND_LIVE_ROPE:
- fprintf(stderr, "(rope)\n");
- break;
- case ZEND_LIVE_NEW:
- fprintf(stderr, "(new)\n");
- break;
- }
- }
- }
- if (op_array->last_try_catch) {
- fprintf(stderr, "EXCEPTION TABLE:\n");
- for (i = 0; i < op_array->last_try_catch; i++) {
- fprintf(stderr,
- " %04u",
- op_array->try_catch_array[i].try_op);
-
- if (op_array->try_catch_array[i].catch_op) {
- fprintf(stderr,
- ", %04u",
- op_array->try_catch_array[i].catch_op);
- } else {
- fprintf(stderr, ", -");
- }
- if (op_array->try_catch_array[i].finally_op) {
- fprintf(stderr,
- ", %04u",
- op_array->try_catch_array[i].finally_op);
- } else {
- fprintf(stderr, ", -");
- }
- if (op_array->try_catch_array[i].finally_end) {
- fprintf(stderr,
- ", %04u",
- op_array->try_catch_array[i].finally_end);
- } else {
- fprintf(stderr, ", -\n");
- }
- }
- }
- }
-}
-
-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, uint32_t dump_flags)
-{
- 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, dump_flags);
- 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, "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");
- }
- }
-}
diff --git a/ext/opcache/Optimizer/zend_dump.h b/ext/opcache/Optimizer/zend_dump.h
deleted file mode 100644
index 3d8ff7ad7a..0000000000
--- a/ext/opcache/Optimizer/zend_dump.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Bytecode Visualisation |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef ZEND_DUMP_H
-#define ZEND_DUMP_H
-
-#include "zend_ssa.h"
-#include "zend_dfg.h"
-
-#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0)
-#define ZEND_DUMP_RC_INFERENCE (1<<1)
-#define ZEND_DUMP_CFG (1<<2)
-#define ZEND_DUMP_SSA (1<<3)
-#define ZEND_DUMP_LIVE_RANGES (1<<4)
-
-BEGIN_EXTERN_C()
-
-void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
-void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op);
-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, uint32_t dump_flags);
-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, uint32_t dump_flags);
-void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
-void zend_dump_op_array_name(const zend_op_array *op_array);
-void zend_dump_const(const zval *zv);
-void zend_dump_ht(HashTable *ht);
-
-END_EXTERN_C()
-
-#endif /* ZEND_DUMP_H */
diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c
deleted file mode 100644
index f987e1063f..0000000000
--- a/ext/opcache/Optimizer/zend_func_info.c
+++ /dev/null
@@ -1,974 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Func Info |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- | Xinchen Hui <laruence@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "zend_compile.h"
-#include "zend_extensions.h"
-#include "zend_ssa.h"
-#include "zend_optimizer_internal.h"
-#include "zend_inference.h"
-#include "zend_call_graph.h"
-#include "zend_func_info.h"
-#include "zend_inference.h"
-#ifdef _WIN32
-#include "win32/ioutil.h"
-#endif
-
-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, (MAY_BE_RC1 | (info)), NULL}
-#define FN(name, info) \
- {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
-#define FR(name, info) \
- {name, sizeof(name)-1, (MAY_BE_REF | (info)), NULL}
-#define FX(name, info) \
- {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | (info)), NULL}
-#define FC(name, callback) \
- {name, sizeof(name)-1, 0, callback}
-
-static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
-{
- if (!call_info->send_unpack
- && (call_info->num_args == 2 || call_info->num_args == 3)
- && ssa
- && !(ssa->cfg.flags & ZEND_SSA_TSSA)) {
- zend_op_array *op_array = call_info->caller_op_array;
- uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline,
- &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]);
- uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
- &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
- uint32_t t3 = 0;
- uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED;
-
- if (call_info->num_args == 3) {
- t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline,
- &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]);
- }
- 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;
- }
- if ((t1 & (MAY_BE_DOUBLE|MAY_BE_STRING))
- || (t2 & (MAY_BE_DOUBLE|MAY_BE_STRING))
- || (t3 & (MAY_BE_DOUBLE|MAY_BE_STRING))) {
- tmp |= MAY_BE_ARRAY_OF_DOUBLE;
- }
- if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) {
- if ((t3 & MAY_BE_ANY) != MAY_BE_DOUBLE) {
- tmp |= MAY_BE_ARRAY_OF_LONG;
- }
- }
- return tmp;
- } else {
- /* May throw */
- return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
- }
-}
-
-#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 */
- F1("zend_version", MAY_BE_STRING),
- FN("func_get_arg", UNKNOWN_INFO),
- FN("func_get_args", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
- F1("get_class_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
- F1("get_class_methods", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("get_included_files", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- FN("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
- F0("restore_error_handler", MAY_BE_TRUE),
- F0("restore_exception_handler", MAY_BE_TRUE),
- F1("get_declared_traits", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("get_declared_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("get_declared_interfaces", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("get_defined_functions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
- F1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
- F1("get_resource_type", MAY_BE_STRING),
- F1("get_defined_constants", 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_backtrace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
- F1("get_loaded_extensions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("get_extension_funcs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
-
- /* ext/standard */
- FN("constant", 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),
- F1("bin2hex", MAY_BE_STRING),
- F1("hex2bin", MAY_BE_FALSE | MAY_BE_STRING),
-#if HAVE_NANOSLEEP
- F1("time_nanosleep", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
-#endif
-#if HAVE_STRPTIME
- F1("strptime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
-#endif
- F1("wordwrap", MAY_BE_STRING),
- F1("htmlspecialchars", MAY_BE_STRING),
- F1("htmlentities", MAY_BE_STRING),
- F1("get_html_translation_table", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("sha1", MAY_BE_STRING),
- F1("sha1_file", MAY_BE_FALSE | MAY_BE_STRING),
- F1("md5", MAY_BE_STRING),
- F1("md5_file", MAY_BE_FALSE | MAY_BE_STRING),
- F1("iptcparse", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
- F1("iptcembed", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("getimagesize", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("getimagesizefromstring", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("image_type_to_mime_type", MAY_BE_STRING),
- F1("image_type_to_extension", MAY_BE_FALSE | MAY_BE_STRING),
- F0("phpinfo", MAY_BE_TRUE),
- F1("phpversion", MAY_BE_FALSE | MAY_BE_STRING),
- F0("phpcredits", MAY_BE_TRUE),
- F1("php_sapi_name", MAY_BE_FALSE | MAY_BE_STRING),
- F1("php_uname", MAY_BE_STRING),
- F1("php_ini_scanned_files", MAY_BE_FALSE | MAY_BE_STRING),
- F1("php_ini_loaded_file", MAY_BE_FALSE | MAY_BE_STRING),
- F1("strtok", MAY_BE_FALSE | MAY_BE_STRING),
- F1("strrev", MAY_BE_STRING),
- F1("hebrev", MAY_BE_STRING),
- F1("basename", MAY_BE_STRING),
- F1("dirname", MAY_BE_STRING),
- F1("pathinfo", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("stripslashes", MAY_BE_STRING),
- F1("stripcslashes", MAY_BE_STRING),
- F1("strstr", MAY_BE_FALSE | MAY_BE_STRING),
- F1("stristr", MAY_BE_FALSE | MAY_BE_STRING),
- F1("strrchr", MAY_BE_FALSE | MAY_BE_STRING),
- F1("str_shuffle", MAY_BE_STRING),
- F1("str_word_count", MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("str_split", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING),
- FN("substr_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
- F1("quotemeta", MAY_BE_STRING),
- F1("ucwords", MAY_BE_STRING),
- F1("addcslashes", MAY_BE_STRING),
- FN("str_replace", 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),
- FN("str_ireplace", 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_STRING),
- F1("count_chars", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("chunk_split", MAY_BE_STRING),
- F1("strip_tags", MAY_BE_STRING),
- F1("explode", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("localeconv", 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_FALSE | MAY_BE_STRING),
-#endif
- F1("soundex", MAY_BE_STRING),
- F1("chr", MAY_BE_STRING),
- F1("str_getcsv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
- F1("strchr", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sprintf", MAY_BE_STRING),
- F1("vsprintf", MAY_BE_STRING),
- F1("sscanf", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
- F1("fscanf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
- F1("parse_url", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | 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_STRING),
- F1("urldecode", MAY_BE_STRING),
- F1("rawurlencode", MAY_BE_STRING),
- F1("rawurldecode", MAY_BE_STRING),
- F1("http_build_query", MAY_BE_STRING),
-#if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
- F1("readlink", MAY_BE_FALSE | MAY_BE_STRING),
-#endif
- F1("exec", MAY_BE_FALSE | MAY_BE_STRING),
- F1("system", MAY_BE_FALSE | MAY_BE_STRING),
- F1("escapeshellcmd", MAY_BE_STRING),
- F1("escapeshellarg", MAY_BE_STRING),
- F0("passthru", MAY_BE_NULL | MAY_BE_FALSE),
- 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_get_status", 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
- F1("random_bytes", MAY_BE_STRING),
-#if HAVE_GETSERVBYPORT
- F1("getservbyport", MAY_BE_FALSE | MAY_BE_STRING),
-#endif
-#if HAVE_GETPROTOBYNUMBER
- F1("getprotobynumber", MAY_BE_FALSE | MAY_BE_STRING),
-#endif
- F1("base64_decode", MAY_BE_FALSE | MAY_BE_STRING),
- F1("base64_encode", MAY_BE_STRING),
- F1("password_hash", MAY_BE_STRING),
- F1("password_get_info", 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),
- F1("convert_uuencode", MAY_BE_STRING),
- F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING),
- F1("pow", MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_OBJECT),
- F1("decbin", MAY_BE_STRING),
- F1("decoct", MAY_BE_STRING),
- F1("dechex", MAY_BE_STRING),
- F1("base_convert", MAY_BE_STRING),
- F1("number_format", MAY_BE_STRING),
-#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("long2ip", MAY_BE_FALSE | MAY_BE_STRING),
- F1("getenv", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
-#ifdef HAVE_PUTENV
-#endif
- F1("getopt", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | 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_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
-#endif
-#ifdef HAVE_GETTIMEOFDAY
- F1("microtime", MAY_BE_DOUBLE | MAY_BE_STRING),
- F1("gettimeofday", MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
-#endif
-#ifdef HAVE_GETRUSAGE
- F1("getrusage", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
-#endif
-#ifdef HAVE_GETTIMEOFDAY
- F1("uniqid", MAY_BE_STRING),
-#endif
- F1("quoted_printable_decode", MAY_BE_STRING),
- F1("quoted_printable_encode", MAY_BE_STRING),
- F1("get_current_user", MAY_BE_STRING),
- F1("get_cfg_var", 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("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
- FN("call_user_func", UNKNOWN_INFO),
- FN("call_user_func_array", UNKNOWN_INFO),
- FN("call_user_method", UNKNOWN_INFO),
- FN("call_user_method_array", UNKNOWN_INFO),
- FN("forward_static_call", UNKNOWN_INFO),
- FN("forward_static_call_array", UNKNOWN_INFO),
- F1("serialize", MAY_BE_STRING),
- FN("unserialize", UNKNOWN_INFO),
- F1("var_export", MAY_BE_NULL | MAY_BE_STRING),
- F1("print_r", MAY_BE_TRUE | MAY_BE_STRING),
- F0("register_shutdown_function", MAY_BE_NULL | MAY_BE_FALSE),
- F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("show_source", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("php_strip_whitespace", MAY_BE_STRING),
- F1("ini_get_all", MAY_BE_FALSE | 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_alter", MAY_BE_FALSE | MAY_BE_STRING),
- F1("get_include_path", MAY_BE_FALSE | MAY_BE_STRING),
- F1("set_include_path", MAY_BE_FALSE | MAY_BE_STRING),
- F1("headers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("parse_ini_file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | 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_ANY | 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("gethostbyaddr", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gethostbyname", MAY_BE_STRING),
- F1("gethostbynamel", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
-#ifdef HAVE_GETHOSTNAME
- F1("gethostname", MAY_BE_FALSE | MAY_BE_STRING),
-#endif
-#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC
-# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS
- F1("dns_get_record", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
-# endif
-#endif
- F1("popen", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING),
- F1("fgets", MAY_BE_FALSE | MAY_BE_STRING),
- F1("fread", MAY_BE_FALSE | MAY_BE_STRING),
- F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
- F1("tempnam", MAY_BE_FALSE | MAY_BE_STRING),
- F1("tmpfile", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("file_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
- F1("stream_context_create", MAY_BE_RESOURCE),
- F1("stream_context_get_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- FN("stream_context_get_options", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- FN("stream_context_get_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
- FN("stream_context_set_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
- FN("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE),
- FN("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE),
- 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),
-#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_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
- F1("fgetcsv", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
- F1("get_meta_tags", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("stream_get_meta_data", 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_get_wrappers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("stream_get_transports", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("stream_resolve_include_path", MAY_BE_FALSE | MAY_BE_STRING),
- F1("get_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
- F1("socket_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- F1("realpath", MAY_BE_FALSE | MAY_BE_STRING),
- F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
- FN("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pack", MAY_BE_STRING),
- F1("unpack", 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),
- F1("crypt", MAY_BE_STRING),
- FN("opendir", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("getcwd", MAY_BE_FALSE | MAY_BE_STRING),
- F1("readdir", MAY_BE_FALSE | MAY_BE_STRING),
- F1("dir", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("scandir", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
-#ifdef HAVE_GLOB
- F1("glob", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
-#endif
- F1("filetype", MAY_BE_FALSE | MAY_BE_STRING),
- F1("stat", MAY_BE_FALSE | 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_FALSE | 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("realpath_cache_get", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
-#ifdef HAVE_SYSLOG_H
- F0("syslog", MAY_BE_TRUE),
- F0("closelog", MAY_BE_TRUE),
-#endif
- F1("metaphone", MAY_BE_STRING),
- F1("ob_get_flush", MAY_BE_FALSE | MAY_BE_STRING),
- F1("ob_get_clean", MAY_BE_FALSE | MAY_BE_STRING),
- F1("ob_get_status", 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_list_handlers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F0("array_walk", MAY_BE_TRUE),
- F0("array_walk_recursive", MAY_BE_TRUE),
- F0("arsort", MAY_BE_TRUE),
- F0("asort", MAY_BE_TRUE),
- F0("krsort", MAY_BE_TRUE),
- F0("ksort", MAY_BE_TRUE),
- F0("shuffle", MAY_BE_TRUE),
- F0("sort", MAY_BE_TRUE),
- F0("usort", MAY_BE_TRUE),
- F0("uasort", MAY_BE_TRUE),
- F0("uksort", MAY_BE_TRUE),
- FN("end", UNKNOWN_INFO),
- FN("prev", UNKNOWN_INFO),
- FN("next", UNKNOWN_INFO),
- FN("reset", UNKNOWN_INFO),
- FN("current", UNKNOWN_INFO),
- FN("min", UNKNOWN_INFO),
- FN("max", UNKNOWN_INFO),
- F1("compact", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_fill", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
- F1("array_fill_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- FC("range", zend_range_info),
- FN("array_pop", UNKNOWN_INFO),
- FN("array_shift", UNKNOWN_INFO),
- F1("array_splice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_slice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_replace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_replace_recursive", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- FN("array_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
- FN("array_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_count_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
- F1("array_column", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_reverse", 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_flip", 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_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- FN("array_rand", MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("array_intersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_intersect_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_intersect_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_uintersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_intersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_uintersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_intersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_uintersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_diff_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_diff_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_udiff", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_diff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_udiff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_diff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_udiff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_filter", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_chunk", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("array_combine", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("pos", UNKNOWN_INFO),
- F1("assert_options", MAY_BE_NULL | 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("str_rot13", MAY_BE_STRING),
- F1("stream_get_filters", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_OBJECT),
- F1("stream_bucket_new", MAY_BE_OBJECT),
- F1("sys_get_temp_dir", MAY_BE_STRING),
-
- /* ext/date */
- F1("date", MAY_BE_STRING),
- F1("gmdate", MAY_BE_STRING),
- F1("strftime", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING),
- F1("localtime", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
- F1("getdate", 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_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- F1("date_parse_from_format", 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_STRING),
- F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("date_diff", MAY_BE_OBJECT),
- F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("timezone_name_get", MAY_BE_STRING),
- F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING),
- 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_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_STRING),
- 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_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG),
-
- /* ext/preg */
- FN("preg_replace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
- FN("preg_replace_callback", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
- F1("preg_filter", MAY_BE_NULL | 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 | MAY_BE_ARRAY_OF_ARRAY),
- F1("preg_grep", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
-
- /* ext/mysqli */
- F1("mysqli_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
- F0("mysqli_close", MAY_BE_TRUE),
- F1("mysqli_connect_error", MAY_BE_NULL | MAY_BE_STRING),
- F1("mysqli_get_client_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("mysqli_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
- F1("mysqli_get_links_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
- F1("mysqli_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
- F1("mysqli_get_charset", MAY_BE_NULL | MAY_BE_OBJECT),
- F1("mysqli_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
- F1("mysqli_fetch_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- F1("mysqli_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
- F1("mysqli_fetch_object", MAY_BE_NULL | MAY_BE_OBJECT),
- F1("mysqli_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
- F1("mysqli_character_set_name", MAY_BE_STRING),
- F0("mysqli_debug", MAY_BE_TRUE),
- F1("mysqli_error", MAY_BE_STRING),
- F1("mysqli_reap_async_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
- F1("mysqli_stmt_get_result", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_stmt_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
- F1("mysqli_stmt_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_fetch_field", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_fetch_fields", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_OBJECT),
- F1("mysqli_fetch_field_direct", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_fetch_lengths", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("mysqli_fetch_row", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
- F1("mysqli_get_client_info", MAY_BE_NULL | MAY_BE_STRING),
- F1("mysqli_get_host_info", MAY_BE_STRING),
- F1("mysqli_get_server_info", MAY_BE_STRING),
- F1("mysqli_info", MAY_BE_NULL | MAY_BE_STRING),
- F1("mysqli_init", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_insert_id", MAY_BE_LONG | MAY_BE_STRING),
- F1("mysqli_num_rows", MAY_BE_LONG | MAY_BE_STRING),
- F1("mysqli_prepare", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_real_escape_string", MAY_BE_STRING),
- F1("mysqli_stmt_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
- F1("mysqli_stmt_insert_id", MAY_BE_LONG | MAY_BE_STRING),
- F1("mysqli_stmt_num_rows", MAY_BE_LONG | MAY_BE_STRING),
- F1("mysqli_sqlstate", MAY_BE_STRING),
- F0("mysqli_ssl_set", MAY_BE_TRUE),
- F1("mysqli_stat", MAY_BE_FALSE | MAY_BE_STRING),
- F1("mysqli_stmt_error", MAY_BE_STRING),
- F1("mysqli_stmt_init", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_stmt_result_metadata", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_stmt_sqlstate", MAY_BE_STRING),
- F1("mysqli_store_result", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("mysqli_use_result", MAY_BE_FALSE | MAY_BE_OBJECT),
-
- /* ext/curl */
- F1("curl_init", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("curl_copy_handle", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("curl_version", 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_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_STRING),
- F1("curl_strerror", MAY_BE_NULL | MAY_BE_STRING),
- F1("curl_multi_strerror", MAY_BE_NULL | MAY_BE_STRING),
- F1("curl_escape", MAY_BE_FALSE | MAY_BE_STRING),
- F1("curl_unescape", MAY_BE_FALSE | MAY_BE_STRING),
- F1("curl_multi_init", MAY_BE_OBJECT),
- F1("curl_multi_info_read", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_OBJECT),
- F1("curl_share_init", MAY_BE_OBJECT),
- F1("curl_file_create", MAY_BE_OBJECT),
-
- /* ext/mbstring */
- F1("mb_convert_case", MAY_BE_STRING),
- F1("mb_strtoupper", MAY_BE_STRING),
- F1("mb_strtolower", MAY_BE_STRING),
- F1("mb_language", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("mb_internal_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("mb_http_input", MAY_BE_FALSE | MAY_BE_STRING| MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("mb_http_output", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("mb_detect_order", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("mb_substitute_character", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_STRING),
- F1("mb_output_handler", MAY_BE_STRING),
- F1("mb_preferred_mime_name", MAY_BE_FALSE | MAY_BE_STRING),
- 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", MAY_BE_STRING),
- F1("mb_strcut", MAY_BE_STRING),
- F1("mb_strimwidth", MAY_BE_STRING),
- F1("mb_convert_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
- F1("mb_detect_encoding", 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_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("mb_convert_kana", MAY_BE_STRING),
- F1("mb_encode_mimeheader", MAY_BE_STRING),
- F1("mb_decode_mimeheader", MAY_BE_STRING),
- F1("mb_convert_variables", MAY_BE_FALSE | MAY_BE_STRING),
- F1("mb_encode_numericentity", MAY_BE_STRING),
- F1("mb_decode_numericentity", MAY_BE_STRING),
- 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_regex_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("mb_regex_set_options", MAY_BE_STRING),
- F1("mb_ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
- F1("mb_eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
- F1("mb_ereg_replace_callback", MAY_BE_NULL | 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_search_pos", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("mb_ereg_search_regs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
- F1("mb_ereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
-
- /* ext/iconv */
- F1("iconv", MAY_BE_FALSE | MAY_BE_STRING),
- F1("iconv_get_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING),
- 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_FALSE | MAY_BE_STRING),
- F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
- F1("json_last_error_msg", MAY_BE_STRING),
-
- /* ext/xml */
- F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING),
- F1("xml_parser_get_option", MAY_BE_LONG | MAY_BE_STRING),
- F1("utf8_encode", MAY_BE_STRING),
- F1("utf8_decode", MAY_BE_STRING),
-
- /* ext/zlib */
- F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gzgets", 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("gzfile", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("gzcompress", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gzuncompress", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gzdeflate", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gzinflate", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gzencode", MAY_BE_FALSE | MAY_BE_STRING),
- F1("gzdecode", MAY_BE_FALSE | MAY_BE_STRING),
- F1("zlib_encode", MAY_BE_FALSE | MAY_BE_STRING),
- F1("zlib_decode", MAY_BE_FALSE | MAY_BE_STRING),
- F1("zlib_get_coding_type", MAY_BE_FALSE | MAY_BE_STRING),
- F1("ob_gzhandler", MAY_BE_FALSE | MAY_BE_STRING),
-
- /* ext/hash */
- F1("hash", MAY_BE_FALSE | MAY_BE_STRING),
- F1("hash_file", MAY_BE_FALSE | MAY_BE_STRING),
- F1("hash_hmac", MAY_BE_FALSE | MAY_BE_STRING),
- F1("hash_hmac_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("hash_hmac_file", MAY_BE_FALSE | MAY_BE_STRING),
- F1("hash_hkdf", MAY_BE_STRING),
- F1("hash_init", MAY_BE_OBJECT),
- F1("hash_final", MAY_BE_STRING),
- F1("hash_copy", MAY_BE_OBJECT),
- F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("hash_pbkdf2", MAY_BE_STRING),
- F1("mhash_keygen_s2k", MAY_BE_FALSE | MAY_BE_STRING),
- F1("mhash_get_hash_name", MAY_BE_FALSE | MAY_BE_STRING),
- F1("mhash", MAY_BE_FALSE | MAY_BE_FALSE | MAY_BE_STRING),
-
- /* ext/sodium */
- F1("sodium_crypto_shorthash", MAY_BE_STRING),
- F1("sodium_crypto_secretbox", MAY_BE_STRING),
- F1("sodium_crypto_secretbox_open", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_crypto_generichash", MAY_BE_STRING),
- F1("sodium_crypto_generichash_init", MAY_BE_STRING),
- F0("sodium_crypto_generichash_update", MAY_BE_TRUE),
- F1("sodium_crypto_generichash_final", MAY_BE_STRING),
- F1("sodium_crypto_box_keypair", MAY_BE_STRING),
- F1("sodium_crypto_box_seed_keypair", MAY_BE_STRING),
- F1("sodium_crypto_box_secretkey", MAY_BE_STRING),
- F1("sodium_crypto_box_publickey", MAY_BE_STRING),
- F1("sodium_crypto_box", MAY_BE_STRING),
- F1("sodium_crypto_box_open", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_crypto_box_seal", MAY_BE_STRING),
- F1("sodium_crypto_box_seal_open", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_crypto_sign_keypair", MAY_BE_STRING),
- F1("sodium_crypto_sign_seed_keypair", MAY_BE_STRING),
- F1("sodium_crypto_sign_secretkey", MAY_BE_STRING),
- F1("sodium_crypto_sign_publickey", MAY_BE_STRING),
- F1("sodium_crypto_sign", MAY_BE_STRING),
- F1("sodium_crypto_sign_open", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_crypto_sign_detached", MAY_BE_STRING),
- F1("sodium_crypto_stream", MAY_BE_STRING),
- F1("sodium_crypto_stream_xor", MAY_BE_STRING),
- F1("sodium_crypto_pwhash", MAY_BE_STRING),
- F1("sodium_crypto_pwhash_str", MAY_BE_STRING),
- F1("sodium_crypto_aead_aes256gcm_encrypt", MAY_BE_STRING),
- F1("sodium_crypto_aead_aes256gcm_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_bin2hex", MAY_BE_STRING),
- F1("sodium_hex2bin", MAY_BE_STRING),
- F1("sodium_crypto_scalarmult", MAY_BE_STRING),
- F1("sodium_crypto_kx_seed_keypair", MAY_BE_STRING),
- F1("sodium_crypto_kx_keypair", MAY_BE_STRING),
- F1("sodium_crypto_kx_secretkey", MAY_BE_STRING),
- F1("sodium_crypto_kx_publickey", MAY_BE_STRING),
- F1("sodium_crypto_kx_client_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("sodium_crypto_kx_server_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("sodium_crypto_auth", MAY_BE_STRING),
- F1("sodium_crypto_aead_aes256gcm_keygen", MAY_BE_STRING),
- F1("sodium_crypto_auth_keygen", MAY_BE_STRING),
- F1("sodium_crypto_generichash_keygen", MAY_BE_STRING),
- F1("sodium_crypto_kdf_keygen", MAY_BE_STRING),
- F1("sodium_crypto_secretbox_keygen", MAY_BE_STRING),
- F1("sodium_crypto_shorthash_keygen", MAY_BE_STRING),
- F1("sodium_crypto_stream_keygen", MAY_BE_STRING),
- F1("sodium_crypto_kdf_derive_from_key", MAY_BE_STRING),
- F1("sodium_pad", MAY_BE_STRING),
- F1("sodium_unpad", MAY_BE_STRING),
-
- F1("sodium_crypto_box_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
- F1("sodium_crypto_box_publickey_from_secretkey", MAY_BE_STRING),
- F1("sodium_crypto_sign_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
- F1("sodium_crypto_sign_publickey_from_secretkey", MAY_BE_STRING),
- F1("sodium_crypto_pwhash_scryptsalsa208sha256", MAY_BE_STRING),
- F1("sodium_crypto_pwhash_scryptsalsa208sha256_str", MAY_BE_STRING),
- F1("sodium_crypto_sign_ed25519_sk_to_curve25519", MAY_BE_STRING),
- F1("sodium_crypto_sign_ed25519_pk_to_curve25519", MAY_BE_STRING),
- F1("sodium_crypto_aead_chacha20poly1305_encrypt", MAY_BE_STRING),
- F1("sodium_crypto_aead_chacha20poly1305_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_crypto_aead_chacha20poly1305_ietf_encrypt", MAY_BE_STRING),
- F1("sodium_crypto_aead_chacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_crypto_aead_xchacha20poly1305_ietf_encrypt", MAY_BE_STRING),
- F1("sodium_crypto_aead_xchacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
- F1("sodium_crypto_aead_chacha20poly1305_keygen", MAY_BE_STRING),
- F1("sodium_crypto_aead_chacha20poly1305_ietf_keygen", MAY_BE_STRING),
- F1("sodium_crypto_aead_xchacha20poly1305_ietf_keygen", MAY_BE_STRING),
-
- /* ext/session */
- F1("session_get_cookie_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- F1("session_name", MAY_BE_FALSE | MAY_BE_STRING),
- F1("session_module_name", MAY_BE_FALSE | MAY_BE_STRING),
- F1("session_save_path", MAY_BE_FALSE | MAY_BE_STRING),
- F1("session_create_id", MAY_BE_FALSE | MAY_BE_STRING),
- F1("session_cache_limiter", MAY_BE_FALSE | MAY_BE_STRING),
- F1("session_encode", MAY_BE_FALSE | MAY_BE_STRING),
-
- /* ext/pgsql */
- F1("pg_connect", MAY_BE_FALSE | MAY_BE_RESOURCE),
- FN("pg_pconnect", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pg_dbname", MAY_BE_STRING),
- F1("pg_last_error", MAY_BE_STRING),
- F1("pg_options", MAY_BE_STRING),
- F1("pg_port", MAY_BE_STRING),
- F1("pg_tty", MAY_BE_STRING),
- F1("pg_host", MAY_BE_STRING),
- F1("pg_version", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_NULL),
- F1("pg_parameter_status", MAY_BE_FALSE | MAY_BE_STRING),
- F1("pg_query", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pg_query_params", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pg_prepare", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pg_execute", MAY_BE_FALSE | MAY_BE_RESOURCE),
- FN("pg_last_notice", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY ),
- F1("pg_field_table", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
- F1("pg_field_name", MAY_BE_STRING),
- F1("pg_field_type", MAY_BE_STRING),
- F1("pg_field_type_oid", MAY_BE_LONG | MAY_BE_STRING),
- F1("pg_fetch_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
- F1("pg_fetch_row", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
- F1("pg_fetch_assoc", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
- F1("pg_fetch_array", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
- F1("pg_fetch_object", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("pg_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
- F1("pg_fetch_all_columns", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
- F1("pg_last_oid", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
- F1("pg_lo_create", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
- F1("pg_lo_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pg_lo_read", MAY_BE_FALSE | MAY_BE_STRING),
- F1("pg_lo_import", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
- F1("pg_copy_to", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
- F1("pg_escape_string", MAY_BE_STRING),
- F1("pg_escape_bytea", MAY_BE_STRING),
- F1("pg_unescape_bytea", MAY_BE_STRING),
- F1("pg_escape_literal", MAY_BE_FALSE | MAY_BE_STRING),
- F1("pg_escape_identifier", MAY_BE_FALSE | MAY_BE_STRING),
- F1("pg_result_error", MAY_BE_FALSE | MAY_BE_STRING),
- F1("pg_result_error_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
- F1("pg_get_result", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pg_result_status", MAY_BE_LONG | MAY_BE_STRING),
- F1("pg_get_notify", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
- F1("pg_socket", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("pg_meta_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
- F1("pg_convert", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- F1("pg_insert", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_STRING),
- F1("pg_update", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("pg_delete", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
- F1("pg_select", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
-
- /* ext/bcmath */
- F1("bcadd", MAY_BE_STRING),
- F1("bcsub", MAY_BE_STRING),
- F1("bcmul", MAY_BE_STRING),
- F1("bcdiv", MAY_BE_STRING),
- F1("bcmod", MAY_BE_STRING),
- F1("bcpowmod", MAY_BE_STRING),
- F1("bcpow", MAY_BE_STRING),
- F1("bcsqrt", MAY_BE_STRING),
-
- /* ext/exif */
- F1("exif_tagname", MAY_BE_FALSE | MAY_BE_STRING),
- F1("exif_read_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
- F1("exif_thumbnail", MAY_BE_FALSE | MAY_BE_STRING),
-
- /* ext/filter */
- FN("filter_input", UNKNOWN_INFO),
- FN("filter_var", UNKNOWN_INFO),
- F1("filter_input_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
- F1("filter_var_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
- F1("filter_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
-
- /* ext/gettext */
- F1("textdomain", MAY_BE_STRING),
- F1("gettext", MAY_BE_STRING),
- F1("_", MAY_BE_STRING),
- F1("dgettext", MAY_BE_STRING),
- F1("dcgettext", MAY_BE_STRING),
- F1("bindtextdomain", MAY_BE_FALSE | MAY_BE_STRING),
-#if HAVE_NGETTEXT
- F1("ngettext", MAY_BE_STRING),
-#endif
-#if HAVE_DNGETTEXT
- F1("dcngettext", MAY_BE_STRING),
-#endif
-#if HAVE_BIND_TEXTDOMAIN_CODESET
- F1("bind_textdomain_codeset", MAY_BE_FALSE | MAY_BE_STRING),
-#endif
-
- /* ext/fileinfo */
- F1("finfo_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
- F1("finfo_file", MAY_BE_FALSE | MAY_BE_STRING),
- F1("finfo_buffer", MAY_BE_FALSE | MAY_BE_STRING),
- F1("mime_content_type", MAY_BE_FALSE | MAY_BE_STRING),
-
- /* ext/gd */
- F1("gd_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE),
- F1("imagecreatetruecolor", MAY_BE_FALSE | MAY_BE_OBJECT),
-#ifdef PHP_WIN32
- F1("imagegrabwindow", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagegrabscreen", MAY_BE_FALSE | MAY_BE_OBJECT),
-#endif
- F1("imagerotate", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagecreate", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagecreatefromstring", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagecreatefromgif", MAY_BE_FALSE | MAY_BE_OBJECT),
-#ifdef HAVE_GD_JPG
- F1("imagecreatefromjpeg", MAY_BE_FALSE | MAY_BE_OBJECT),
-#endif
-#ifdef HAVE_GD_PNG
- F1("imagecreatefrompng", MAY_BE_FALSE | MAY_BE_OBJECT),
-#endif
-#ifdef HAVE_GD_WEBP
- F1("imagecreatefromwebp", MAY_BE_FALSE | MAY_BE_OBJECT),
-#endif
- F1("imagecreatefromxbm", MAY_BE_FALSE | MAY_BE_OBJECT),
-#if defined(HAVE_GD_XPM)
- F1("imagecreatefromxpm", MAY_BE_FALSE | MAY_BE_OBJECT),
-#endif
- F1("imagecreatefromwbmp", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagecreatefromgd", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagecreatefromgd2", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagecreatefromgd2part", MAY_BE_FALSE | MAY_BE_OBJECT),
-#if defined(HAVE_GD_BMP)
- F1("imagecreatefrombmp", MAY_BE_FALSE | MAY_BE_OBJECT),
-#endif
- F0("imagecolorset", MAY_BE_NULL | MAY_BE_FALSE),
- F1("imagecolorsforindex", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
- F1("imagegetclip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("imageftbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("imagefttext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("imagettfbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("imagettftext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
- F1("imagecrop", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagecropauto", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imagescale", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imageaffine", MAY_BE_FALSE | MAY_BE_OBJECT),
- F1("imageaffinematrixget", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
- F1("imageaffinematrixconcat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
- F1("imageresolution", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
-
- /* ext/spl */
- F1("class_implements", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("class_parents", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("class_uses", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("iterator_to_array", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
- F1("spl_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
- F1("spl_object_hash", MAY_BE_STRING),
-
-};
-
-static HashTable func_info;
-int zend_func_info_rid = -1;
-
-static uint32_t get_internal_func_info(
- const zend_call_info *call_info, const zend_ssa *ssa, zend_string *lcname) {
- if (call_info->callee_func->common.scope) {
- /* This is a method, not a function. */
- return 0;
- }
-
- zval *zv = zend_hash_find_ex(&func_info, lcname, 1);
- if (!zv) {
- return 0;
- }
-
- func_info_t *info = Z_PTR_P(zv);
- if (info->info_func) {
- return info->info_func(call_info, ssa);
- } else {
- return info->info;
- }
-}
-
-uint32_t zend_get_func_info(
- const zend_call_info *call_info, const zend_ssa *ssa,
- zend_class_entry **ce, zend_bool *ce_is_instanceof)
-{
- uint32_t ret = 0;
- const zend_function *callee_func = call_info->callee_func;
- *ce = NULL;
- *ce_is_instanceof = 0;
-
- if (callee_func->type == ZEND_INTERNAL_FUNCTION) {
- zend_string *lcname = Z_STR_P(CRT_CONSTANT_EX(call_info->caller_op_array, call_info->caller_init_opline, call_info->caller_init_opline->op2));
-
- uint32_t internal_ret = get_internal_func_info(call_info, ssa, lcname);
-#if !ZEND_DEBUG
- if (internal_ret) {
- return internal_ret;
- }
-#endif
-
- if (callee_func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- ret = zend_fetch_arg_info_type(NULL, callee_func->common.arg_info - 1, ce);
- *ce_is_instanceof = 1;
- } else {
-#if 0
- fprintf(stderr, "Unknown internal function '%s'\n", func->common.function_name);
-#endif
- ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
- | MAY_BE_RC1 | MAY_BE_RCN;
- }
- if (callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
- ret |= MAY_BE_REF;
- }
-
-#if ZEND_DEBUG
- if (internal_ret) {
- /* Check whether the func_info information is a subset of the information we can
- * compute from the specified return type, otherwise it contains redundant types. */
- if (internal_ret & ~ret) {
- fprintf(stderr, "Inaccurate func info for %s()\n", ZSTR_VAL(lcname));
- }
- /* Check whether the func info is completely redundant with arginfo.
- * Ignore UNKNOWN_INFO for now. */
- if (internal_ret == ret && (internal_ret & MAY_BE_ANY) != MAY_BE_ANY) {
- fprintf(stderr, "Useless func info for %s()\n", ZSTR_VAL(lcname));
- }
- /* If the return type is not mixed, check that the types match exactly if we exclude
- * RC and array information. */
- uint32_t ret_any = ret & MAY_BE_ANY, internal_ret_any = internal_ret & MAY_BE_ANY;
- if (ret_any != MAY_BE_ANY) {
- uint32_t diff = internal_ret_any ^ ret_any;
- /* Func info may contain "true" types as well as isolated "null" and "false". */
- if (diff && !(diff == MAY_BE_FALSE && (ret & MAY_BE_FALSE))
- && (internal_ret_any & ~(MAY_BE_NULL|MAY_BE_FALSE))) {
- fprintf(stderr, "Incorrect func info for %s()\n", ZSTR_VAL(lcname));
- }
- }
- return internal_ret;
- }
-#endif
- } else {
- // FIXME: the order of functions matters!!!
- zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func);
- if (info) {
- ret = info->return_info.type;
- *ce = info->return_info.ce;
- *ce_is_instanceof = info->return_info.is_instanceof;
- }
- if (!ret) {
- ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
- | MAY_BE_RC1 | MAY_BE_RCN;
- /* For generators RETURN_REFERENCE refers to the yielded values. */
- if ((callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
- && !(callee_func->common.fn_flags & ZEND_ACC_GENERATOR)) {
- ret |= MAY_BE_REF;
- }
- }
- }
- return ret;
-}
-
-int zend_func_info_startup(void)
-{
- size_t i;
-
- if (zend_func_info_rid == -1) {
- zend_func_info_rid = zend_get_resource_handle("Zend Optimizer");
- 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++) {
- zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1);
-
- if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) {
- fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
- }
- zend_string_release_ex(key, 1);
- }
- }
-
- 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;
-}
diff --git a/ext/opcache/Optimizer/zend_func_info.h b/ext/opcache/Optimizer/zend_func_info.h
deleted file mode 100644
index 13dd2e3127..0000000000
--- a/ext/opcache/Optimizer/zend_func_info.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, Func Info |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef ZEND_FUNC_INFO_H
-#define ZEND_FUNC_INFO_H
-
-#include "zend_ssa.h"
-
-/* func/cfg flags */
-#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
-#define ZEND_FUNC_HAS_CALLS (1<<1)
-#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
-#define ZEND_FUNC_NO_LOOPS (1<<3)
-#define ZEND_FUNC_IRREDUCIBLE (1<<4)
-#define ZEND_FUNC_FREE_LOOP_VAR (1<<5)
-#define ZEND_FUNC_RECURSIVE (1<<7)
-#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
-#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
-#define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10)
-#define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11)
-#define ZEND_SSA_TSSA (1<<12) /* used by tracing JIT */
-
-#define ZEND_FUNC_JIT_ON_FIRST_EXEC (1<<13) /* used by JIT */
-#define ZEND_FUNC_JIT_ON_PROF_REQUEST (1<<14) /* used by JIT */
-#define ZEND_FUNC_JIT_ON_HOT_COUNTERS (1<<15) /* used by JIT */
-#define ZEND_FUNC_JIT_ON_HOT_TRACE (1<<16) /* used by JIT */
-
-
-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,
- zend_class_entry **ce, zend_bool *ce_is_instanceof);
-
-int zend_func_info_startup(void);
-int zend_func_info_shutdown(void);
-
-END_EXTERN_C()
-
-#endif /* ZEND_FUNC_INFO_H */
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
deleted file mode 100644
index 0dd905e8f3..0000000000
--- a/ext/opcache/Optimizer/zend_inference.c
+++ /dev/null
@@ -1,4680 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, e-SSA based Type & Range Inference |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#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"
-
-/* The used range inference algorithm is described in:
- * V. Campos, R. Rodrigues, I. de Assis Costa and F. Pereira.
- * "Speed and Precision in Range Analysis", SBLP'12.
- *
- * There are a couple degrees of freedom, we use:
- * * Propagation on SCCs.
- * * e-SSA for live range splitting.
- * * Only intra-procedural inference.
- * * Widening with warmup passes, but without jump sets.
- */
-
-/* Whether to handle symbolic range constraints */
-#define SYM_RANGE
-
-/* Whether to handle negative range constraints */
-/* Negative range inference is buggy, so disabled for now */
-#undef NEG_RANGE
-
-/* Number of warmup passes to use prior to widening */
-#define RANGE_WARMUP_PASSES 16
-
-/* Logging for range inference in general */
-#if 0
-#define LOG_SSA_RANGE(...) fprintf(stderr, __VA_ARGS__)
-#else
-#define LOG_SSA_RANGE(...)
-#endif
-
-/* Logging for negative range constraints */
-#if 0
-#define LOG_NEG_RANGE(...) fprintf(stderr, __VA_ARGS__)
-#else
-#define LOG_NEG_RANGE(...)
-#endif
-
-/* Pop elements in unspecified order from worklist until it is empty */
-#define WHILE_WORKLIST(worklist, len, i) do { \
- zend_bool _done = 0; \
- while (!_done) { \
- _done = 1; \
- ZEND_BITSET_FOREACH(worklist, len, i) { \
- zend_bitset_excl(worklist, i); \
- _done = 0;
-
-#define WHILE_WORKLIST_END() \
- } ZEND_BITSET_FOREACH_END(); \
- } \
-} while (0)
-
-#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 ((uint32_t)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 inline zend_bool add_will_overflow(zend_long a, zend_long b) {
- return (b > 0 && a > ZEND_LONG_MAX - b)
- || (b < 0 && a < ZEND_LONG_MIN - b);
-}
-#if 0
-static inline zend_bool sub_will_overflow(zend_long a, zend_long b) {
- return (b > 0 && a < ZEND_LONG_MIN + b)
- || (b < 0 && a > ZEND_LONG_MAX + b);
-}
-#endif
-
-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;
- ALLOCA_FLAG(dfs_use_heap)
- ALLOCA_FLAG(root_use_heap)
- ALLOCA_FLAG(stack_use_heap)
-
- dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap);
- memset(dfs, -1, sizeof(int) * ssa->vars_count);
- root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap);
- ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap);
-
- /* Find SCCs using Tarjan's algorithm. */
- 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. This results in a topological 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);
- }
- }
-
- ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
- free_alloca(root, root_use_heap);
- free_alloca(dfs, dfs_use_heap);
-
- 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;
- ALLOCA_FLAG(use_heap);
-
- if (!op_array->function_name || !ssa->vars || !ssa->ops) {
- return SUCCESS;
- }
-
- worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
- 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 (!zend_ssa_is_no_val_use(&op_array->opcodes[use], &ssa->ops[use], i)) {
- ssa_vars[i].no_val = 0; /* used directly */
- zend_bitset_incl(worklist, i);
- break;
- }
- use = zend_ssa_next_use(ssa_ops, i, use);
- }
- }
-
- WHILE_WORKLIST(worklist, zend_bitset_len(ssa_vars_count), 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++) {
- ZEND_ASSERT(p->sources[j] >= 0);
- if (ssa->vars[p->sources[j]].no_val) {
- ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */
- zend_bitset_incl(worklist, p->sources[j]);
- }
- }
- }
- }
- } WHILE_WORKLIST_END();
-
- free_alloca(worklist, use_heap);
-
- return SUCCESS;
-}
-/* }}} */
-
-/* From "Hacker's Delight" */
-zend_ulong minOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
-{
- zend_ulong m, temp;
-
- m = Z_UL(1) << (sizeof(zend_ulong) * 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;
-}
-
-zend_ulong maxOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
-{
- zend_ulong m, temp;
-
- m = Z_UL(1) << (sizeof(zend_ulong) * 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;
-}
-
-zend_ulong minAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
-{
- zend_ulong m, temp;
-
- m = Z_UL(1) << (sizeof(zend_ulong) * 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;
-}
-
-zend_ulong maxAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
-{
- zend_ulong m, temp;
-
- m = Z_UL(1) << (sizeof(zend_ulong) * 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;
-}
-
-zend_ulong minXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
-{
- return minAND(a, b, ~d, ~c) | minAND(~b, ~a, c, d);
-}
-
-zend_ulong maxXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong 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(zend_long a, zend_long b, zend_long c, zend_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(zend_long a, zend_long b, zend_long c, zend_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;
- }
-}
-
-static inline zend_bool zend_abs_range(
- zend_long min, zend_long max, zend_long *abs_min, zend_long *abs_max) {
- if (min == ZEND_LONG_MIN) {
- /* Cannot take absolute value of LONG_MIN */
- return 0;
- }
-
- if (min >= 0) {
- *abs_min = min;
- *abs_max = max;
- } else if (max <= 0) {
- *abs_min = -max;
- *abs_max = -min;
- } else {
- /* Range crossing zero */
- *abs_min = 0;
- *abs_max = MAX(max, -min);
- }
-
- return 1;
-}
-
-static inline zend_long safe_shift_left(zend_long n, zend_long s) {
- return (zend_long) ((zend_ulong) n << (zend_ulong) s);
-}
-
-static inline zend_bool shift_left_overflows(zend_long n, zend_long s) {
- /* This considers shifts that shift in the sign bit to be overflowing as well */
- if (n >= 0) {
- return s >= SIZEOF_ZEND_LONG * 8 - 1 || safe_shift_left(n, s) < n;
- } else {
- return s >= SIZEOF_ZEND_LONG * 8 || safe_shift_left(n, s) > n;
- }
-}
-
-/* If b does not divide a exactly, return the two adjacent values between which the real result
- * lies. */
-static void float_div(zend_long a, zend_long b, zend_long *r1, zend_long *r2) {
- *r1 = *r2 = a / b;
- if (a % b != 0) {
- if (*r2 < 0) {
- (*r2)--;
- } else {
- (*r2)++;
- }
- }
-}
-
-static int zend_inference_calc_binary_op_range(
- const zend_op_array *op_array, zend_ssa *ssa,
- zend_op *opline, zend_ssa_op *ssa_op, zend_uchar opcode, zend_ssa_range *tmp) {
- zend_long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4;
-
- switch (opcode) {
- case ZEND_ADD:
- 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 (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- zend_add_will_overflow(op1_min, op2_min)) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- } else {
- tmp->min = op1_min + op2_min;
- }
- if (OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- zend_add_will_overflow(op1_max, op2_max)) {
- tmp->overflow = 1;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->max = op1_max + op2_max;
- }
- return 1;
- }
- break;
- case ZEND_SUB:
- 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 (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_OVERFLOW() ||
- zend_sub_will_overflow(op1_min, op2_max)) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- } else {
- tmp->min = op1_min - op2_max;
- }
- if (OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- zend_sub_will_overflow(op1_max, op2_min)) {
- tmp->overflow = 1;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->max = op1_max - op2_min;
- }
- return 1;
- }
- break;
- case ZEND_MUL:
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- double dummy;
- zend_long t1_overflow, t2_overflow, t3_overflow, t4_overflow;
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- /* Suppress uninit variable warnings, these will only be used if the overflow
- * flags are all false. */
- t1 = t2 = t3 = t4 = 0;
- ZEND_SIGNED_MULTIPLY_LONG(op1_min, op2_min, t1, dummy, t1_overflow);
- ZEND_SIGNED_MULTIPLY_LONG(op1_min, op2_max, t2, dummy, t2_overflow);
- ZEND_SIGNED_MULTIPLY_LONG(op1_max, op2_min, t3, dummy, t3_overflow);
- ZEND_SIGNED_MULTIPLY_LONG(op1_max, op2_max, t4, dummy, t4_overflow);
- (void) dummy;
-
- // FIXME: more careful overflow checks?
- if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() || OP2_RANGE_OVERFLOW() ||
- t1_overflow || t2_overflow || t3_overflow || t4_overflow
- ) {
- tmp->underflow = 1;
- tmp->overflow = 1;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_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 (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) {
- /* If op2 crosses zero, then floating point values close to zero might be
- * possible, which will result in arbitrarily large results. As such, we can't
- * do anything useful in that case. */
- break;
- }
- if (op1_min == ZEND_LONG_MIN && op2_max == -1) {
- /* Avoid ill-defined division, which may trigger SIGFPE. */
- break;
- }
-
- zend_long t1_, t2_, t3_, t4_;
- float_div(op1_min, op2_min, &t1, &t1_);
- float_div(op1_min, op2_max, &t2, &t2_);
- float_div(op1_max, op2_min, &t3, &t3_);
- float_div(op1_max, op2_max, &t4, &t4_);
-
- /* The only case in which division can "overflow" either a division by an absolute
- * value smaller than one, or LONG_MIN / -1 in particular. Both cases have already
- * been excluded above. */
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->underflow = 1;
- tmp->overflow = 1;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->min = MIN(MIN(MIN(t1, t2), MIN(t3, t4)), MIN(MIN(t1_, t2_), MIN(t3_, t4_)));
- tmp->max = MAX(MAX(MAX(t1, t2), MAX(t3, t4)), MAX(MAX(t1_, t2_), MAX(t3_, t4_)));
- }
- return 1;
- }
- break;
- case ZEND_MOD:
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- zend_long op2_abs_min, op2_abs_max;
-
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (!zend_abs_range(op2_min, op2_max, &op2_abs_min, &op2_abs_max)) {
- break;
- }
-
- if (op2_abs_max == 0) {
- /* Always modulus by zero, nothing we can do */
- break;
- }
- if (op2_abs_min == 0) {
- /* Ignore the modulus by zero case, which will throw */
- op2_abs_min++;
- }
-
- if (op1_min >= 0) {
- tmp->min = op1_max < op2_abs_min ? op1_min : 0;
- tmp->max = MIN(op1_max, op2_abs_max - 1);
- } else if (op1_max <= 0) {
- tmp->min = MAX(op1_min, -op2_abs_max + 1);
- tmp->max = op1_min > -op2_abs_min ? op1_max : 0;
- } else {
- tmp->min = MAX(op1_min, -op2_abs_max + 1);
- tmp->max = MIN(op1_max, op2_abs_max - 1);
- }
- }
- return 1;
- }
- break;
- case ZEND_SL:
- if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
-
- /* Shifts by negative numbers will throw, ignore them */
- if (op2_min < 0) {
- op2_min = 0;
- }
- if (op2_max < 0) {
- op2_max = 0;
- }
-
- if (shift_left_overflows(op1_min, op2_max)
- || shift_left_overflows(op1_max, op2_max)) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- t1 = safe_shift_left(op1_min, op2_min);
- t2 = safe_shift_left(op1_min, op2_max);
- t3 = safe_shift_left(op1_max, op2_min);
- t4 = safe_shift_left(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 (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- } else {
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
-
- /* Shifts by negative numbers will throw, ignore them */
- if (op2_min < 0) {
- op2_min = 0;
- }
- if (op2_max < 0) {
- op2_max = 0;
- }
-
- /* Shifts by more than the integer size will be 0 or -1 */
- if (op2_min >= SIZEOF_ZEND_LONG * 8) {
- op2_min = SIZEOF_ZEND_LONG * 8 - 1;
- }
- if (op2_max >= SIZEOF_ZEND_LONG * 8) {
- op2_max = SIZEOF_ZEND_LONG * 8 - 1;
- }
-
- 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 (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_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 (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP2_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW() ||
- OP2_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_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:
- // TODO
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- return 0;
-}
-
-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;
- zend_ssa_op *ssa_op;
-
- if (ssa->vars[var].definition_phi) {
- zend_ssa_phi *p = ssa->vars[var].definition_phi;
- int i;
-
- tmp->underflow = 0;
- tmp->min = ZEND_LONG_MAX;
- tmp->max = ZEND_LONG_MIN;
- tmp->overflow = 0;
- if (p->pi >= 0 && p->has_range_constraint) {
- zend_ssa_range_constraint *constraint = &p->constraint.range;
- if (constraint->negative) {
- if (ssa->var_info[p->sources[0]].has_range) {
- *tmp = ssa->var_info[p->sources[0]].range;
- } else if (narrowing) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- tmp->overflow = 1;
- }
-
-#ifdef NEG_RANGE
- if (constraint->min_ssa_var < 0 &&
- constraint->max_ssa_var < 0 &&
- ssa->var_info[p->ssa_var].has_range) {
- LOG_NEG_RANGE("%s() #%d [%ld..%ld] -> [%ld..%ld]?\n",
- ZSTR_VAL(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);
- if (constraint->negative == NEG_USE_LT &&
- tmp->max >= constraint->range.min) {
- tmp->overflow = 0;
- tmp->max = constraint->range.min - 1;
- LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max);
- } else if (constraint->negative == NEG_USE_GT &&
- tmp->min <= constraint->range.max) {
- tmp->underflow = 0;
- tmp->min = constraint->range.max + 1;
- LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max);
- }
- }
-#endif
- } else if (ssa->var_info[p->sources[0]].has_range) {
- /* intersection */
- *tmp = ssa->var_info[p->sources[0]].range;
- if (constraint->min_ssa_var < 0) {
- tmp->underflow = constraint->range.underflow && tmp->underflow;
- tmp->min = MAX(constraint->range.min, tmp->min);
-#ifdef SYM_RANGE
- } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
- tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow && tmp->underflow;
- if (!add_will_overflow(ssa->var_info[constraint->min_ssa_var].range.min, constraint->range.min)) {
- tmp->min = MAX(ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min, tmp->min);
- }
-#endif
- }
- if (constraint->max_ssa_var < 0) {
- tmp->max = MIN(constraint->range.max, tmp->max);
- tmp->overflow = constraint->range.overflow && tmp->overflow;
-#ifdef SYM_RANGE
- } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
- if (!add_will_overflow(ssa->var_info[constraint->max_ssa_var].range.max, constraint->range.max)) {
- tmp->max = MIN(ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max, tmp->max);
- }
- tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow && tmp->overflow;
-#endif
- }
- } else if (narrowing) {
- if (constraint->min_ssa_var < 0) {
- tmp->underflow = constraint->range.underflow;
- tmp->min = constraint->range.min;
-#ifdef SYM_RANGE
- } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
- if (add_will_overflow(ssa->var_info[constraint->min_ssa_var].range.min, constraint->range.min)) {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- } else {
- tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow;
- tmp->min = ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min;
- }
-#endif
- } else {
- tmp->underflow = 1;
- tmp->min = ZEND_LONG_MIN;
- }
- if (constraint->max_ssa_var < 0) {
- tmp->max = constraint->range.max;
- tmp->overflow = constraint->range.overflow;
-#ifdef SYM_RANGE
- } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
- if (add_will_overflow(ssa->var_info[constraint->max_ssa_var].range.max, constraint->range.max)) {
- tmp->overflow = 1;
- tmp->max = ZEND_LONG_MAX;
- } else {
- tmp->max = ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max;
- tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow;
- }
-#endif
- } else {
- tmp->max = ZEND_LONG_MAX;
- tmp->overflow = 1;
- }
- }
- } else {
- for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
- ZEND_ASSERT(p->sources[i] >= 0);
- if (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 = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- tmp->overflow = 1;
- }
- }
- }
- return (tmp->min <= tmp->max);
- } else if (ssa->vars[var].definition < 0) {
- if (var < op_array->last_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;
- ssa_op = &ssa->ops[line];
-
- return zend_inference_propagate_range(op_array, ssa, opline, ssa_op, var, tmp);
-}
-
-int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp)
-{
- zend_long op1_min, op2_min, op1_max, op2_max;
-
- tmp->underflow = 0;
- tmp->overflow = 0;
- switch (opline->opcode) {
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- case ZEND_DIV:
- case ZEND_MOD:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- if (ssa_op->result_def == var) {
- return zend_inference_calc_binary_op_range(
- op_array, ssa, opline, ssa_op, opline->opcode, tmp);
- }
- break;
-
- case ZEND_BW_NOT:
- if (ssa_op->result_def == var) {
- if (OP1_HAS_RANGE()) {
- if (OP1_RANGE_UNDERFLOW() ||
- OP1_RANGE_OVERFLOW()) {
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_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_op->op1_def == var) {
- if (ssa_op->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;
- }
- }
- } else if (ssa_op->result_def == var) {
- 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 = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- return 1;
- }
- }
- }
- break;
- case ZEND_BOOL:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- if (ssa_op->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_op->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_op->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_op->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_max &&
- 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_op->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_max ||
- op1_min != op2_max);
- return 1;
- } else {
- tmp->min = 0;
- tmp->max = 1;
- return 1;
- }
- }
- break;
- case ZEND_IS_SMALLER:
- if (ssa_op->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_op->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_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_COPY_TMP:
- if (ssa_op->op1_def == var) {
- if (ssa_op->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;
- }
- }
- }
- if (ssa_op->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_op->result_def == var) {
- tmp->min = 0;
- tmp->max = 1;
- return 1;
- }
- break;
- case ZEND_SEND_VAR:
- if (ssa_op->op1_def == var) {
- if (ssa_op->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_op->op1_def == var || ssa_op->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 < ZEND_LONG_MAX) {
- tmp->max++;
- } else {
- tmp->overflow = 1;
- }
- if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
- tmp->min++;
- }
- return 1;
- }
- }
- break;
- case ZEND_PRE_DEC:
- if (ssa_op->op1_def == var || ssa_op->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 > ZEND_LONG_MIN) {
- tmp->min--;
- } else {
- tmp->underflow = 1;
- }
- if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
- tmp->max--;
- }
- return 1;
- }
- }
- break;
- case ZEND_POST_INC:
- if (ssa_op->op1_def == var || ssa_op->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_op->result_def == var) {
- return 1;
- }
- if (tmp->max < ZEND_LONG_MAX) {
- tmp->max++;
- } else {
- tmp->overflow = 1;
- }
- if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
- tmp->min++;
- }
- return 1;
- }
- }
- break;
- case ZEND_POST_DEC:
- if (ssa_op->op1_def == var || ssa_op->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_op->result_def == var) {
- return 1;
- }
- if (tmp->min > ZEND_LONG_MIN) {
- tmp->min--;
- } else {
- tmp->underflow = 1;
- }
- if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
- tmp->max--;
- }
- return 1;
- }
- }
- break;
- case ZEND_UNSET_DIM:
- case ZEND_UNSET_OBJ:
- if (ssa_op->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_op->op1_def == var || ssa_op->op2_def == var || ssa_op->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:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- if ((ssa_op+1)->op1_def == var) {
- opline++;
- ssa_op++;
- 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_OP:
- if (opline->extended_value != ZEND_CONCAT
- && opline->extended_value != ZEND_POW) {
- if (ssa_op->op1_def == var || ssa_op->result_def == var) {
- return zend_inference_calc_binary_op_range(
- op_array, ssa, opline, ssa_op,
- opline->extended_value, tmp);
- }
- }
- break;
- case ZEND_OP_DATA:
- if (ssa_op->op1_def == var) {
- if ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
- (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
- (opline-1)->opcode == ZEND_ASSIGN_STATIC_PROP ||
- (opline-1)->opcode == ZEND_ASSIGN_DIM_OP ||
- (opline-1)->opcode == ZEND_ASSIGN_OBJ_OP ||
- (opline-1)->opcode == ZEND_ASSIGN_STATIC_PROP_OP) {
- 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_op->result_def == var) {
- if (op_array->arg_info &&
- opline->op1.num <= op_array->num_args) {
- zend_type type = op_array->arg_info[opline->op1.num-1].type;
- uint32_t mask = ZEND_TYPE_PURE_MASK_WITHOUT_NULL(type);
- if (mask == MAY_BE_LONG) {
- tmp->underflow = 0;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- tmp->overflow = 0;
- return 1;
- } else if (mask == MAY_BE_BOOL) {
- tmp->underflow = 0;
- tmp->min = 0;
- tmp->max = 1;
- tmp->overflow = 0;
- return 1;
- }
- }
- }
- break;
- case ZEND_STRLEN:
- if (ssa_op->result_def == var) {
-#if SIZEOF_ZEND_LONG == 4
- /* The length of a string is a non-negative integer. However, on 32-bit
- * platforms overflows into negative lengths may occur, so it's better
- * to not assume any particular range. */
- tmp->min = ZEND_LONG_MIN;
-#else
- tmp->min = 0;
-#endif
- tmp->max = ZEND_LONG_MAX;
- return 1;
- }
- break;
- case ZEND_FUNC_NUM_ARGS:
- tmp->min = 0;
- tmp->max = ZEND_LONG_MAX;
- return 1;
- case ZEND_COUNT:
- /* count() on Countable objects may return negative numbers */
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- return 1;
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- if (ssa_op->result_def == var) {
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- zend_call_info *call_info;
- if (!func_info || !func_info->call_map) {
- break;
- }
-
- call_info = func_info->call_map[opline - op_array->opcodes];
- if (!call_info) {
- break;
- }
- 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 = ZEND_LONG_MIN;
- tmp->max = ZEND_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, zend_long min, zend_long max, zend_bool overflow)
-{
- if (underflow) {
- min = ZEND_LONG_MIN;
- }
- if (overflow) {
- max = ZEND_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;
- LOG_SSA_RANGE(" change range (init SCC %2d) %2d [%s%ld..%ld%s]\n", ssa->vars[var].scc, var, (underflow?"-- ":""), min, max, (overflow?" ++":""));
-}
-
-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 = ZEND_LONG_MIN;
- }
- if (r->overflow ||
- var_info->range.overflow ||
- r->max > var_info->range.max) {
- r->overflow = 1;
- r->max = ZEND_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)) {
- LOG_SSA_RANGE(" change range (widening SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
- 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 = ZEND_LONG_MIN;
- }
- if (r->overflow) {
- r->max = ZEND_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)) {
- LOG_SSA_RANGE(" change range (narrowing SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
- 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);
- int j, n;
- zend_ssa_range tmp;
- ALLOCA_FLAG(use_heap)
- zend_bitset worklist = do_alloca(sizeof(zend_ulong) * worklist_len * 2, use_heap);
- zend_bitset visited = worklist + worklist_len;
-#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_WARMUP_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_WORKLIST(worklist, worklist_len, 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->has_range_constraint &&
- ssa->vars[j].definition_phi->constraint.range.negative &&
- ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0 &&
- ssa->vars[j].definition_phi->constraint.range.max_ssa_var < 0) {
- zend_ssa_range_constraint *constraint =
- &ssa->vars[j].definition_phi->constraint.range;
- if (tmp.min == ssa->var_info[j].range.min &&
- tmp.max == ssa->var_info[j].range.max) {
- if (constraint->negative == NEG_INIT) {
- LOG_NEG_RANGE("#%d INVARIANT\n", j);
- 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 < constraint->range.min) {
- if (constraint->negative == NEG_INIT ||
- constraint->negative == NEG_INVARIANT) {
- LOG_NEG_RANGE("#%d LT\n", j);
- constraint->negative = NEG_USE_LT;
-//???NEG
- } else if (constraint->negative == NEG_USE_GT) {
- LOG_NEG_RANGE("#%d UNKNOWN\n", j);
- 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 > constraint->range.max) {
- if (constraint->negative == NEG_INIT ||
- constraint->negative == NEG_INVARIANT) {
- LOG_NEG_RANGE("#%d GT\n", j);
- constraint->negative = NEG_USE_GT;
-//???NEG
- } else if (constraint->negative == NEG_USE_LT) {
- LOG_NEG_RANGE("#%d UNKNOWN\n", j);
- constraint->negative = NEG_UNKNOWN;
- }
- } else {
- LOG_NEG_RANGE("#%d UNKNOWN\n", j);
- constraint->negative = NEG_UNKNOWN;
- }
- }
-#endif
- if (zend_inference_narrowing_meet(&ssa->var_info[j], &tmp)) {
- LOG_SSA_RANGE(" change range (warmup %2d SCC %2d) %2d [%s%ld..%ld%s]\n", n, scc, j, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
- zend_bitset_incl(visited, j);
- FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR_1);
- }
- }
- } WHILE_WORKLIST_END();
- }
- free_alloca(worklist, use_heap);
-}
-
-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;
- int *next_scc_var;
- int *scc_var;
- zend_ssa_phi *p;
- zend_ssa_range tmp;
- int scc, j;
- ALLOCA_FLAG(use_heap);
-
- worklist = do_alloca(
- ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len) +
- ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count) +
- sizeof(int) * ssa->sccs, use_heap);
- next_scc_var = (int*)((char*)worklist + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len));
- scc_var = (int*)((char*)next_scc_var + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count));
-
- LOG_SSA_RANGE("Range Inference\n");
-
- /* 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, ZEND_LONG_MIN, ZEND_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_WARMUP_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_WORKLIST(worklist, worklist_len, j) {
- if (zend_ssa_range_widening(op_array, ssa, j, scc)) {
- FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
- }
- } WHILE_WORKLIST_END();
-
- /* initialize missing ranges */
- 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, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
- FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
- }
- }
-
- /* widening (second round) */
- WHILE_WORKLIST(worklist, worklist_len, j) {
- if (zend_ssa_range_widening(op_array, ssa, j, scc)) {
- FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
- }
- } WHILE_WORKLIST_END();
-
- /* Add all SCC entry variables into worklist for narrowing */
- for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) {
- if (ssa->vars[j].definition_phi
- && ssa->vars[j].definition_phi->pi < 0) {
- /* narrowing Phi functions first */
- zend_ssa_range_narrowing(op_array, ssa, j, scc);
- }
- zend_bitset_incl(worklist, j);
- }
-
- /* narrowing */
- WHILE_WORKLIST(worklist, worklist_len, 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
- }
- } WHILE_WORKLIST_END();
- }
- }
-
- free_alloca(worklist, use_heap);
-
- return SUCCESS;
-}
-/* }}} */
-
-static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) {
- if (alias == HTTP_RESPONSE_HEADER_ALIAS) {
- return MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_RC1 | MAY_BE_RCN;
- } else {
- return 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;
- }
-}
-
-#define UPDATE_SSA_TYPE(_type, _var) \
- do { \
- uint32_t __type = (_type) & ~MAY_BE_GUARD; \
- int __var = (_var); \
- if (__type & MAY_BE_REF) { \
- __type |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; \
- } \
- if (__var >= 0) { \
- zend_ssa_var *__ssa_var = &ssa_vars[__var]; \
- if (__ssa_var->var < op_array->last_var) { \
- if (__type & (MAY_BE_REF|MAY_BE_RCN)) { \
- __type |= MAY_BE_RC1 | MAY_BE_RCN; \
- } \
- if ((__type & MAY_BE_RC1) && (__type & MAY_BE_STRING)) {\
- /* TODO: support for array keys and ($str . "")*/ \
- __type |= MAY_BE_RCN; \
- } \
- if (__ssa_var->alias) { \
- __type |= get_ssa_alias_types(__ssa_var->alias); \
- } \
- } \
- if (ssa_var_info[__var].type != __type) { \
- if (ssa_var_info[__var].type & ~__type) { \
- emit_type_narrowing_warning(op_array, ssa, __var); \
- return FAILURE; \
- } \
- ssa_var_info[__var].type = __type; \
- if (update_worklist) { \
- 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); \
- if (update_worklist) { \
- add_usages(op_array, ssa, worklist, var); \
- } \
- } \
- /*zend_bitset_excl(worklist, var);*/ \
- } \
- } while (0)
-
-#define COPY_SSA_OBJ_TYPE(from_var, to_var) do { \
- if ((from_var) >= 0 && (ssa_var_info[(from_var)].type & MAY_BE_OBJECT) \
- && ssa_var_info[(from_var)].ce) { \
- UPDATE_SSA_OBJ_TYPE(ssa_var_info[(from_var)].ce, \
- ssa_var_info[(from_var)].is_instanceof, (to_var)); \
- } else { \
- UPDATE_SSA_OBJ_TYPE(NULL, 0, (to_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;
- zend_ssa_op *op;
-
- do {
- op = ssa->ops + use;
- if (op->result_def >= 0) {
- zend_bitset_incl(worklist, op->result_def);
- }
- if (op->op1_def >= 0) {
- zend_bitset_incl(worklist, op->op1_def);
- }
- if (op->op2_def >= 0) {
- zend_bitset_incl(worklist, op->op2_def);
- }
- if (op_array->opcodes[use].opcode == ZEND_OP_DATA) {
- op--;
- if (op->result_def >= 0) {
- zend_bitset_incl(worklist, op->result_def);
- }
- if (op->op1_def >= 0) {
- zend_bitset_incl(worklist, op->op1_def);
- }
- if (op->op2_def >= 0) {
- zend_bitset_incl(worklist, op->op2_def);
- }
- }
- use = zend_ssa_next_use(ssa->ops, var, use);
- } while (use >= 0);
- }
-}
-
-static void emit_type_narrowing_warning(const zend_op_array *op_array, zend_ssa *ssa, int var)
-{
- int def_op_num = ssa->vars[var].definition;
- const zend_op *def_opline = def_op_num >= 0 ? &op_array->opcodes[def_op_num] : NULL;
- const char *def_op_name = def_opline ? zend_get_opcode_name(def_opline->opcode) : "PHI";
- zend_error(E_WARNING, "Narrowing occurred during type inference of %s. Please file a bug report on bugs.php.net", def_op_name);
-}
-
-uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert)
-{
- uint32_t tmp = 0;
-
- if (t1 & MAY_BE_OBJECT) {
- if (!write) {
- /* can't be REF because of ZVAL_COPY_DEREF() usage */
- tmp |= MAY_BE_ANY | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- } else {
- tmp |= 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 (write) {
- tmp |= MAY_BE_INDIRECT;
- }
- }
- if (t1 & MAY_BE_ARRAY) {
- if (insert) {
- tmp |= MAY_BE_NULL;
- } else {
- tmp |= 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 (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- if (!write) {
- /* can't be REF because of ZVAL_COPY_DEREF() usage */
- tmp |= MAY_BE_RCN;
- if ((op_type & (IS_VAR|IS_TMP_VAR)) && (t1 & MAY_BE_RC1)) {
- tmp |= MAY_BE_RC1;
- }
- } else 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 (write) {
- tmp |= MAY_BE_INDIRECT;
- }
- }
- if (t1 & MAY_BE_STRING) {
- tmp |= MAY_BE_STRING | MAY_BE_RC1;
- if (write) {
- tmp |= MAY_BE_NULL;
- }
- }
- if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
- tmp |= MAY_BE_NULL;
- if (write) {
- tmp |= MAY_BE_INDIRECT;
- }
- }
- if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) {
- if (!write) {
- tmp |= MAY_BE_NULL;
- }
- }
- return tmp;
-}
-
-static uint32_t assign_dim_result_type(
- uint32_t arr_type, uint32_t dim_type, uint32_t value_type, zend_uchar dim_op_type) {
- uint32_t tmp = arr_type & ~(MAY_BE_RC1|MAY_BE_RCN);
-
- if (arr_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
- tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
- tmp |= MAY_BE_ARRAY|MAY_BE_RC1;
- }
- if (tmp & (MAY_BE_ARRAY|MAY_BE_STRING)) {
- tmp |= MAY_BE_RC1;
- }
- if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- }
- if (tmp & MAY_BE_ARRAY) {
- /* Only add key type if we have a value type. We want to maintain the invariant that a
- * key type exists iff a value type exists even in dead code that may use empty types. */
- if (value_type & (MAY_BE_ANY|MAY_BE_UNDEF)) {
- if (value_type & MAY_BE_UNDEF) {
- tmp |= MAY_BE_ARRAY_OF_NULL;
- }
- if (dim_op_type == IS_UNUSED) {
- tmp |= MAY_BE_ARRAY_KEY_LONG;
- } else {
- if (dim_type & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
- tmp |= MAY_BE_ARRAY_KEY_LONG;
- }
- if (dim_type & MAY_BE_STRING) {
- tmp |= MAY_BE_ARRAY_KEY_STRING;
- if (dim_op_type != IS_CONST) {
- // FIXME: numeric string
- tmp |= MAY_BE_ARRAY_KEY_LONG;
- }
- }
- if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) {
- tmp |= MAY_BE_ARRAY_KEY_STRING;
- }
- }
- }
- /* Only add value type if we have a key type. It might be that the key type is illegal
- * for arrays. */
- if (tmp & MAY_BE_ARRAY_KEY_ANY) {
- tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
- }
- }
- return tmp;
-}
-
-/* For binary ops that have compound assignment operators */
-static uint32_t binary_op_result_type(
- zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, int result_var,
- zend_long optimization_level) {
- uint32_t tmp = 0;
- uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
- uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
-
- if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) {
- /* Handle potentially overloaded operators.
- * This could be made more precise by checking the class type, if known. */
- if ((t1_type & MAY_BE_OBJECT) || (t2_type & MAY_BE_OBJECT)) {
- /* This is somewhat GMP specific. */
- tmp |= MAY_BE_OBJECT | MAY_BE_FALSE | MAY_BE_RC1;
- }
- }
-
- switch (opcode) {
- case ZEND_ADD:
- if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
- if (result_var < 0 ||
- !ssa->var_info[result_var].has_range ||
- ssa->var_info[result_var].range.underflow ||
- ssa->var_info[result_var].range.overflow) {
- /* may overflow */
- tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
- } else {
- tmp |= MAY_BE_LONG;
- }
- } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
- tmp |= MAY_BE_DOUBLE;
- } else if (t1_type == MAY_BE_ARRAY && t2_type == MAY_BE_ARRAY) {
- tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
- 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_type & MAY_BE_ARRAY) && (t2_type & MAY_BE_ARRAY)) {
- tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
- 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);
- }
- }
- break;
- case ZEND_SUB:
- case ZEND_MUL:
- if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
- if (result_var < 0 ||
- !ssa->var_info[result_var].has_range ||
- ssa->var_info[result_var].range.underflow ||
- ssa->var_info[result_var].range.overflow) {
- /* may overflow */
- tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
- } else {
- tmp |= MAY_BE_LONG;
- }
- } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
- tmp |= MAY_BE_DOUBLE;
- } else {
- tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
- }
- break;
- case ZEND_DIV:
- case ZEND_POW:
- if (t1_type == MAY_BE_DOUBLE || t2_type == 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 */
- break;
- case ZEND_MOD:
- tmp |= MAY_BE_LONG;
- /* Division by zero results in an exception, so it doesn't need any special handling */
- break;
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- if ((t1_type & MAY_BE_STRING) && (t2_type & MAY_BE_STRING)) {
- tmp |= MAY_BE_STRING | MAY_BE_RC1;
- }
- if ((t1_type & ~MAY_BE_STRING) || (t2_type & ~MAY_BE_STRING)) {
- tmp |= MAY_BE_LONG;
- }
- break;
- case ZEND_SL:
- case ZEND_SR:
- tmp |= MAY_BE_LONG;
- break;
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- /* TODO: +MAY_BE_OBJECT ??? */
- tmp = MAY_BE_STRING | MAY_BE_RC1 | MAY_BE_RCN;
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- return tmp;
-}
-
-static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) {
- zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
- if (ce) {
- return ce;
- }
-
- ce = zend_hash_find_ptr(CG(class_table), lcname);
- if (ce && ce->type == ZEND_INTERNAL_CLASS) {
- return ce;
- }
-
- return NULL;
-}
-
-static uint32_t zend_convert_type_declaration_mask(uint32_t type_mask) {
- uint32_t result_mask = type_mask & MAY_BE_ANY;
- if (type_mask & MAY_BE_VOID) {
- result_mask |= MAY_BE_NULL;
- }
- if (type_mask & MAY_BE_CALLABLE) {
- result_mask |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- }
- if (type_mask & MAY_BE_ITERABLE) {
- result_mask |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- }
- if (type_mask & MAY_BE_STATIC) {
- result_mask |= MAY_BE_OBJECT;
- }
- if (type_mask & MAY_BE_ARRAY) {
- result_mask |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- }
- return result_mask;
-}
-
-uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce)
-{
- uint32_t tmp;
-
- *pce = NULL;
- if (!ZEND_TYPE_IS_SET(arg_info->type)) {
- return MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_RC1|MAY_BE_RCN;
- }
-
- tmp = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(arg_info->type));
- if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
- tmp |= MAY_BE_OBJECT;
- /* As we only have space to store one CE, we use a plain object type for class unions. */
- if (ZEND_TYPE_HAS_NAME(arg_info->type)) {
- zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(arg_info->type));
- *pce = get_class_entry(script, lcname);
- zend_string_release_ex(lcname, 0);
- }
- }
- if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- }
- return tmp;
-}
-
-static zend_property_info *lookup_prop_info(zend_class_entry *ce, zend_string *name, zend_class_entry *scope) {
- zend_property_info *prop_info;
-
- /* If the class is linked, reuse the precise runtime logic. */
- if ((ce->ce_flags & ZEND_ACC_LINKED)
- && (!scope || (scope->ce_flags & ZEND_ACC_LINKED))) {
- zend_class_entry *prev_scope = EG(fake_scope);
- EG(fake_scope) = scope;
- prop_info = zend_get_property_info(ce, name, 1);
- EG(fake_scope) = prev_scope;
- if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO) {
- return prop_info;
- }
- return NULL;
- }
-
- /* Otherwise, handle only some safe cases */
- prop_info = zend_hash_find_ptr(&ce->properties_info, name);
- if (prop_info &&
- ((prop_info->ce == scope) ||
- (!scope && (prop_info->flags & ZEND_ACC_PUBLIC)))
- ) {
- return prop_info;
- }
- return NULL;
-}
-
-static zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op)
-{
- zend_property_info *prop_info = NULL;
- if (opline->op2_type == IS_CONST) {
- zend_class_entry *ce = NULL;
-
- if (opline->op1_type == IS_UNUSED) {
- ce = op_array->scope;
- } else if (ssa_op->op1_use >= 0) {
- ce = ssa->var_info[ssa_op->op1_use].ce;
- }
- if (ce) {
- prop_info = lookup_prop_info(ce,
- Z_STR_P(CRT_CONSTANT(opline->op2)),
- op_array->scope);
- if (prop_info && (prop_info->flags & ZEND_ACC_STATIC)) {
- prop_info = NULL;
- }
- }
- }
- return prop_info;
-}
-
-static zend_property_info *zend_fetch_static_prop_info(const zend_script *script, const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline)
-{
- zend_property_info *prop_info = NULL;
- if (opline->op1_type == IS_CONST) {
- zend_class_entry *ce = NULL;
- if (opline->op2_type == IS_UNUSED) {
- int fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK;
- switch (fetch_type) {
- case ZEND_FETCH_CLASS_SELF:
- case ZEND_FETCH_CLASS_STATIC:
- /* We enforce that static property types cannot change during inheritance, so
- * handling static the same way as self here is legal. */
- ce = op_array->scope;
- break;
- case ZEND_FETCH_CLASS_PARENT:
- if (op_array->scope && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) {
- ce = op_array->scope->parent;
- }
- break;
- }
- } else if (opline->op2_type == IS_CONST) {
- zval *zv = CRT_CONSTANT(opline->op2);
- ce = get_class_entry(script, Z_STR_P(zv + 1));
- }
-
- if (ce) {
- zval *zv = CRT_CONSTANT(opline->op1);
- prop_info = lookup_prop_info(ce, Z_STR_P(zv), op_array->scope);
- if (prop_info && !(prop_info->flags & ZEND_ACC_STATIC)) {
- prop_info = NULL;
- }
- }
- }
- return prop_info;
-}
-
-static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_info *prop_info, zend_class_entry **pce)
-{
- if (pce) {
- *pce = NULL;
- }
- if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) {
- uint32_t type = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(prop_info->type));
-
- if (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- type |= MAY_BE_RC1 | MAY_BE_RCN;
- }
- if (ZEND_TYPE_HAS_CLASS(prop_info->type)) {
- type |= MAY_BE_OBJECT;
- if (pce) {
- if (ZEND_TYPE_HAS_CE(prop_info->type)) {
- *pce = ZEND_TYPE_CE(prop_info->type);
- } else if (ZEND_TYPE_HAS_NAME(prop_info->type)) {
- zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(prop_info->type));
- *pce = get_class_entry(script, lcname);
- zend_string_release(lcname);
- }
- }
- }
- return type;
- }
- return MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_RC1 | MAY_BE_RCN;
-}
-
-static zend_always_inline int _zend_update_type_info(
- const zend_op_array *op_array,
- zend_ssa *ssa,
- const zend_script *script,
- zend_bitset worklist,
- zend_op *opline,
- zend_ssa_op *ssa_op,
- const zend_op **ssa_opcodes,
- zend_long optimization_level,
- zend_bool update_worklist)
-{
- uint32_t t1, t2;
- uint32_t tmp, orig;
- 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--;
- ssa_op--;
- }
-
- t1 = OP1_INFO();
- t2 = OP2_INFO();
-
- /* If one of the operands cannot have any type, this means the operand derives from
- * unreachable code. Propagate the empty result early, so that that the following
- * code may assume that operands have at least one type. */
- if (!(t1 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS))
- || !(t2 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS))) {
- tmp = 0;
- if (ssa_op->result_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- if (ssa_op->op1_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- if (ssa_op->op2_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
- }
- return 1;
- }
-
- switch (opline->opcode) {
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- case ZEND_DIV:
- case ZEND_POW:
- case ZEND_MOD:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_CONCAT:
- tmp = binary_op_result_type(ssa, opline->opcode, t1, t2, ssa_op->result_def, optimization_level);
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_BW_NOT:
- tmp = 0;
- if (t1 & MAY_BE_STRING) {
- tmp |= MAY_BE_STRING | MAY_BE_RC1;
- }
- if (t1 & (MAY_BE_ANY-MAY_BE_STRING)) {
- tmp |= MAY_BE_LONG;
- }
- if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) {
- if (t1 & MAY_BE_OBJECT) {
- /* Potentially overloaded operator. */
- tmp |= MAY_BE_OBJECT | MAY_BE_RC1;
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_BEGIN_SILENCE:
- UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->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_CASE_STRICT:
- case ZEND_BOOL:
- case ZEND_ISSET_ISEMPTY_CV:
- 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:
- case ZEND_IN_ARRAY:
- case ZEND_ARRAY_KEY_EXISTS:
- UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_op->result_def);
- break;
- case ZEND_CAST:
- if (ssa_op->op1_def >= 0) {
- tmp = t1;
- if ((t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) &&
- (opline->extended_value == IS_ARRAY ||
- opline->extended_value == IS_OBJECT)) {
- tmp |= MAY_BE_RCN;
- } else if ((t1 & MAY_BE_STRING) &&
- opline->extended_value == IS_STRING) {
- tmp |= MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- tmp = 1 << opline->extended_value;
- if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- if ((tmp & MAY_BE_ANY) == (t1 & MAY_BE_ANY)) {
- tmp |= (t1 & MAY_BE_RC1) | MAY_BE_RCN;
- } else if ((opline->extended_value == IS_ARRAY ||
- opline->extended_value == IS_OBJECT) &&
- (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- } else if (opline->extended_value == IS_STRING &&
- (t1 & (MAY_BE_STRING|MAY_BE_OBJECT))) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- } else {
- tmp |= MAY_BE_RC1;
- }
- }
- 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) | ((t1 & MAY_BE_ANY) ? MAY_BE_ARRAY_PACKED : 0);
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_QM_ASSIGN:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_COPY_TMP:
- if (ssa_op->op1_def >= 0) {
- tmp = t1;
- if (t1 & (MAY_BE_RC1|MAY_BE_REF)) {
- tmp |= MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- tmp = t1 & ~(MAY_BE_UNDEF|MAY_BE_REF);
- if (t1 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
- tmp |= (t1 & (MAY_BE_RC1|MAY_BE_RCN));
- if (opline->op1_type == IS_CV) {
- tmp |= MAY_BE_RCN;
- }
- }
- if (opline->opcode != ZEND_QM_ASSIGN) {
- /* COALESCE and JMP_SET result can't be null */
- tmp &= ~MAY_BE_NULL;
- if (opline->opcode == ZEND_JMP_SET) {
- /* JMP_SET result can't be false either */
- tmp &= ~MAY_BE_FALSE;
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
- break;
- case ZEND_JMP_NULL:
- if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR) {
- tmp = MAY_BE_NULL;
- } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
- tmp = MAY_BE_FALSE;
- } else {
- ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
- tmp = MAY_BE_TRUE;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- {
- zend_property_info *prop_info = NULL;
- orig = 0;
- tmp = 0;
- if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
- prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
- orig = t1;
- t1 = zend_fetch_prop_type(script, prop_info, &ce);
- t2 = OP1_DATA_INFO();
- } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
- if (t1 & MAY_BE_ARRAY_OF_REF) {
- tmp |= MAY_BE_REF;
- }
- orig = t1;
- t1 = zend_array_element_type(t1, opline->op1_type, 1, 0);
- t2 = OP1_DATA_INFO();
- } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP) {
- prop_info = zend_fetch_static_prop_info(script, op_array, ssa, opline);
- t1 = zend_fetch_prop_type(script, prop_info, &ce);
- t2 = OP1_DATA_INFO();
- } else {
- if (t1 & MAY_BE_REF) {
- tmp |= MAY_BE_REF;
- }
- }
-
- tmp |= binary_op_result_type(
- ssa, opline->extended_value, t1, t2,
- opline->opcode == ZEND_ASSIGN_OP ? ssa_op->op1_def : -1, optimization_level);
- if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY)) {
- tmp |= MAY_BE_RC1;
- }
- if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- }
-
- if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
- if (opline->op1_type == IS_CV) {
- orig = assign_dim_result_type(orig, OP2_INFO(), tmp, opline->op2_type);
- UPDATE_SSA_TYPE(orig, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
- if (opline->op1_type == IS_CV) {
- orig = (orig & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN;
- UPDATE_SSA_TYPE(orig, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP) {
- /* Nothing to do */
- } else {
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- ce = NULL;
- if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
- if (opline->op2_type == IS_UNUSED) {
- /* When appending to an array and the LONG_MAX key is already used
- * null will be returned. */
- tmp |= MAY_BE_NULL;
- }
- if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) {
- /* Arrays and objects cannot be used as keys. */
- tmp |= MAY_BE_NULL;
- }
- if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) {
- /* null and false are implicitly converted to array, anything else
- * results in a null return value. */
- tmp |= MAY_BE_NULL;
- }
- } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
- /* The return value must also satisfy the property type */
- if (prop_info) {
- tmp &= zend_fetch_prop_type(script, prop_info, NULL);
- }
- } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP) {
- /* The return value must also satisfy the property type */
- if (prop_info) {
- tmp &= zend_fetch_prop_type(script, prop_info, NULL);
- }
- }
- tmp &= ~MAY_BE_REF;
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
- }
- }
- break;
- }
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- tmp = 0;
- if (t1 & MAY_BE_REF) {
- tmp |= MAY_BE_REF;
- }
- if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
- tmp |= MAY_BE_RC1;
- if (ssa_op->result_def >= 0) {
- tmp |= MAY_BE_RCN;
- }
- }
- if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
- if (!ssa_var_info[ssa_op->op1_use].has_range ||
- (opline->opcode == ZEND_PRE_DEC &&
- (ssa_var_info[ssa_op->op1_use].range.underflow ||
- ssa_var_info[ssa_op->op1_use].range.min == ZEND_LONG_MIN)) ||
- (opline->opcode == ZEND_PRE_INC &&
- (ssa_var_info[ssa_op->op1_use].range.overflow ||
- ssa_var_info[ssa_op->op1_use].range.max == ZEND_LONG_MAX))) {
- /* may overflow */
- tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
- } else {
- tmp |= MAY_BE_LONG;
- }
- } else {
- if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
- if (opline->opcode == ZEND_PRE_INC) {
- tmp |= MAY_BE_LONG;
- } else {
- tmp |= MAY_BE_NULL;
- }
- }
- 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_op->op1_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- break;
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- if (ssa_op->result_def >= 0) {
- tmp = 0;
- if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
- tmp |= MAY_BE_RC1|MAY_BE_RCN;
- }
- tmp |= t1 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RCN);
- if (t1 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- tmp = 0;
- 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_UNDEF)) == MAY_BE_LONG) {
- if (!ssa_var_info[ssa_op->op1_use].has_range ||
- (opline->opcode == ZEND_POST_DEC &&
- (ssa_var_info[ssa_op->op1_use].range.underflow ||
- ssa_var_info[ssa_op->op1_use].range.min == ZEND_LONG_MIN)) ||
- (opline->opcode == ZEND_POST_INC &&
- (ssa_var_info[ssa_op->op1_use].range.overflow ||
- ssa_var_info[ssa_op->op1_use].range.max == ZEND_LONG_MAX))) {
- /* may overflow */
- tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
- } else {
- tmp |= MAY_BE_LONG;
- }
- } else {
- if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
- if (opline->opcode == ZEND_POST_INC) {
- tmp |= MAY_BE_LONG;
- } else {
- tmp |= MAY_BE_NULL;
- }
- }
- 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_op->op1_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- break;
- case ZEND_ASSIGN_DIM:
- if (opline->op1_type == IS_CV) {
- tmp = assign_dim_result_type(t1, t2, OP1_DATA_INFO(), opline->op2_type);
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- tmp = 0;
- if (t1 & MAY_BE_STRING) {
- tmp |= MAY_BE_STRING;
- }
- if (t1 & ((MAY_BE_ANY|MAY_BE_UNDEF) - 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));
-
- if (opline->op2_type == IS_UNUSED) {
- /* When appending to an array and the LONG_MAX key is already used
- * null will be returned. */
- tmp |= MAY_BE_NULL;
- }
- if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) {
- /* Arrays and objects cannot be used as keys. */
- tmp |= MAY_BE_NULL;
- }
- if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) {
- /* undef, null and false are implicitly converted to array, anything else
- * results in a null return value. */
- tmp |= MAY_BE_NULL;
- }
- }
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- if (t1 & MAY_BE_OBJECT) {
- tmp |= MAY_BE_REF;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- if ((ssa_op+1)->op1_def >= 0) {
- opline++;
- ssa_op++;
- tmp = OP1_INFO();
- if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
- if (tmp & MAY_BE_RC1) {
- tmp |= MAY_BE_RCN;
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- break;
- case ZEND_ASSIGN_OBJ:
- if (opline->op1_type == IS_CV) {
- tmp = (t1 & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN;
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- // TODO: If there is no __set we might do better
- tmp = zend_fetch_prop_type(script,
- zend_fetch_prop_info(op_array, ssa, opline, ssa_op), &ce);
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
- }
- }
- if ((ssa_op+1)->op1_def >= 0) {
- opline++;
- ssa_op++;
- tmp = OP1_INFO();
- if (tmp & MAY_BE_RC1) {
- tmp |= MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP:
- if (ssa_op->result_def >= 0) {
- tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_RC1 | MAY_BE_RCN;
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- if ((ssa_op+1)->op1_def >= 0) {
- opline++;
- ssa_op++;
- tmp = OP1_INFO();
- if (tmp & MAY_BE_RC1) {
- tmp |= MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- break;
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- if (opline->op1_type == IS_CV) {
- tmp = (t1 & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN;
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- // TODO: ???
- tmp = 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_op->result_def);
- }
- break;
- case ZEND_ASSIGN:
- if (ssa_op->op2_def >= 0) {
- tmp = t2;
- if (tmp & MAY_BE_RC1) {
- tmp |= MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
- }
- tmp = t2 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
- if (t2 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- if (t1 & MAY_BE_REF) {
- tmp |= MAY_BE_REF;
- }
- if (t2 & MAY_BE_REF) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
- tmp |= t2 & (MAY_BE_RC1|MAY_BE_RCN);
- } else if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
- tmp |= MAY_BE_RCN;
- }
- if (RETURN_VALUE_USED(opline) && (tmp & MAY_BE_RC1)) {
- tmp |= MAY_BE_RCN;
- }
- if (ssa_op->op1_def >= 0) {
- if (ssa_var_info[ssa_op->op1_def].use_as_double) {
- tmp &= ~MAY_BE_LONG;
- tmp |= MAY_BE_DOUBLE;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- UPDATE_SSA_TYPE(tmp & ~MAY_BE_REF, ssa_op->result_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->result_def);
- }
- break;
- case ZEND_ASSIGN_REF:
-// TODO: ???
- if (opline->op2_type == IS_CV) {
- tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
- if (t2 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
- }
- if (opline->op2_type == IS_VAR && opline->extended_value == ZEND_RETURNS_FUNCTION) {
- tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF;
- } else {
- tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
- }
- if (t2 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- if (ssa_op->result_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- break;
- case ZEND_ASSIGN_OBJ_REF:
- if (opline->op1_type == IS_CV) {
- tmp = t1;
- if (tmp & MAY_BE_OBJECT) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
-
- t2 = OP1_DATA_INFO();
- if ((opline+1)->op1_type == IS_VAR && (opline->extended_value & ZEND_RETURNS_FUNCTION)) {
- tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF;
- } else {
- tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
- }
- if (t2 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- if (ssa_op->result_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- if ((opline+1)->op1_type == IS_CV) {
- opline++;
- ssa_op++;
- tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
- if (t2 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP_REF:
- if ((opline+1)->op1_type == IS_CV) {
- opline++;
- ssa_op++;
- UPDATE_SSA_TYPE(MAY_BE_REF, ssa_op->op1_def);
- }
- break;
- case ZEND_BIND_GLOBAL:
- tmp = MAY_BE_REF | MAY_BE_ANY
- | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- break;
- case ZEND_BIND_STATIC:
- tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
- | ((opline->extended_value & ZEND_BIND_REF) ? MAY_BE_REF : (MAY_BE_RC1 | MAY_BE_RCN));
- if (opline->extended_value & ZEND_BIND_IMPLICIT) {
- tmp |= MAY_BE_UNDEF;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- break;
- case ZEND_SEND_VAR:
- if (ssa_op->op1_def >= 0) {
- tmp = t1;
- if (t1 & (MAY_BE_RC1|MAY_BE_REF)) {
- tmp |= MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- break;
- case ZEND_BIND_LEXICAL:
- if (ssa_op->op2_def >= 0) {
- if (opline->extended_value & ZEND_BIND_REF) {
- tmp = t2 | MAY_BE_REF;
- } else {
- tmp = t2 & ~(MAY_BE_RC1|MAY_BE_RCN);
- if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) {
- tmp |= MAY_BE_RCN;
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->op2_def);
- }
- break;
- case ZEND_YIELD:
- if (ssa_op->op1_def >= 0) {
- if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
- tmp = t1 | MAY_BE_REF;
- } else {
- tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN);
- if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
- tmp |= MAY_BE_RCN;
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
- | MAY_BE_RC1 | MAY_BE_RCN;
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- break;
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- if (ssa_op->op1_def >= 0) {
- tmp = (t1 & MAY_BE_UNDEF)|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_op->op1_def);
- }
- break;
- case ZEND_SEND_REF:
- if (ssa_op->op1_def >= 0) {
- tmp = 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_op->op1_def);
- }
- break;
- case ZEND_SEND_UNPACK:
- if (ssa_op->op1_def >= 0) {
- tmp = t1;
- if (t1 & MAY_BE_ARRAY) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- if (t1 & MAY_BE_ARRAY_OF_ANY) {
- /* SEND_UNPACK may acquire references into the array */
- tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- }
- if (t1 & MAY_BE_OBJECT) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- break;
- case ZEND_FAST_CONCAT:
- case ZEND_ROPE_INIT:
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- UPDATE_SSA_TYPE(MAY_BE_STRING|MAY_BE_RC1|MAY_BE_RCN, ssa_op->result_def);
- break;
- case ZEND_RECV:
- case ZEND_RECV_INIT:
- case ZEND_RECV_VARIADIC:
- {
- /* Typehinting */
- zend_arg_info *arg_info = &op_array->arg_info[opline->op1.num-1];
-
- ce = NULL;
- tmp = zend_fetch_arg_info_type(script, arg_info, &ce);
- if (ZEND_ARG_SEND_MODE(arg_info)) {
- tmp |= MAY_BE_REF;
- }
-
- if (opline->opcode == ZEND_RECV_VARIADIC) {
- uint32_t elem_type = tmp & MAY_BE_REF
- ? MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF
- : (tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
- tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|elem_type;
- ce = NULL;
- }
-
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
- } else {
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
- }
- break;
- }
- case ZEND_DECLARE_ANON_CLASS:
- UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_op->result_def);
- if (script && (ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT(opline->op1)))) != NULL) {
- UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def);
- }
- break;
- case ZEND_FETCH_CLASS:
- UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_op->result_def);
- if (opline->op2_type == IS_UNUSED) {
- switch (opline->op1.num & ZEND_FETCH_CLASS_MASK) {
- case ZEND_FETCH_CLASS_SELF:
- if (op_array->scope) {
- UPDATE_SSA_OBJ_TYPE(op_array->scope, 0, ssa_op->result_def);
- } else {
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
- }
- break;
- case ZEND_FETCH_CLASS_PARENT:
- if (op_array->scope && op_array->scope->parent && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) {
- UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_op->result_def);
- } else {
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
- }
- break;
- case ZEND_FETCH_CLASS_STATIC:
- default:
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
- break;
- }
- } else if (opline->op2_type == IS_CONST) {
- zval *zv = CRT_CONSTANT(opline->op2);
- if (Z_TYPE_P(zv) == IS_STRING) {
- ce = get_class_entry(script, Z_STR_P(zv+1));
- UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def);
- } else {
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
- }
- } else {
- COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->result_def);
- }
- break;
- case ZEND_NEW:
- tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT;
- if (opline->op1_type == IS_CONST &&
- (ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1))) != NULL) {
- UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def);
- } else if ((t1 & MAY_BE_CLASS) && ssa_op->op1_use >= 0 && ssa_var_info[ssa_op->op1_use].ce) {
- UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_op->op1_use].ce, ssa_var_info[ssa_op->op1_use].is_instanceof, ssa_op->result_def);
- } else {
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_CLONE:
- UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_op->result_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
- break;
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- if (ssa_op->op1_def >= 0) {
- if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
- tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
- if (t1 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- } else if ((t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN)) == MAY_BE_REF) {
- tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
- if (t1 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_NULL;
- }
- } else if (t1 & MAY_BE_REF) {
- tmp = (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_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- tmp = MAY_BE_RC1|MAY_BE_ARRAY;
- if (ssa_op->result_use >= 0) {
- tmp |= ssa_var_info[ssa_op->result_use].type;
- }
- if (opline->op1_type != IS_UNUSED) {
- tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
- if (t1 & MAY_BE_UNDEF) {
- tmp |= MAY_BE_ARRAY_OF_NULL;
- }
- if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
- tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- }
- 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|MAY_BE_RESOURCE)) {
- 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_UNDEF | MAY_BE_NULL)) {
- tmp |= MAY_BE_ARRAY_KEY_STRING;
- }
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- break;
- case ZEND_ADD_ARRAY_UNPACK:
- tmp = ssa_var_info[ssa_op->result_use].type;
- ZEND_ASSERT(tmp & MAY_BE_ARRAY);
- /* Ignore string keys as they will throw. */
- if (t1 & MAY_BE_ARRAY_KEY_LONG) {
- tmp |= MAY_BE_ARRAY_KEY_LONG | (t1 & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF));
- }
- if (t1 & MAY_BE_OBJECT) {
- tmp |= MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_UNSET_CV:
- tmp = MAY_BE_UNDEF;
- if (!op_array->function_name) {
- /* In global scope, we know nothing */
- tmp |= MAY_BE_REF;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- break;
- case ZEND_UNSET_DIM:
- case ZEND_UNSET_OBJ:
- if (ssa_op->op1_def >= 0) {
- UPDATE_SSA_TYPE(t1, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- break;
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- if (ssa_op->op1_def >= 0) {
- tmp = t1;
- if (opline->opcode == ZEND_FE_RESET_RW) {
- tmp |= MAY_BE_REF;
- } else if (t1 & MAY_BE_RC1) {
- tmp |= MAY_BE_RCN;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- if (opline->opcode == ZEND_FE_RESET_RW) {
-//???
- tmp = MAY_BE_REF | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT));
- } else {
- tmp = MAY_BE_RC1 | MAY_BE_RCN | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
- }
- /* The result is set to UNDEF for invalid foreach inputs. */
- if ((t1 & (MAY_BE_ANY | MAY_BE_UNDEF)) & ~(MAY_BE_ARRAY | MAY_BE_OBJECT)) {
- tmp |= MAY_BE_UNDEF;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- tmp = t2 & MAY_BE_REF;
- if (t1 & MAY_BE_OBJECT) {
- if (opline->opcode == ZEND_FE_FETCH_RW) {
- tmp |= 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_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- }
- if (t1 & MAY_BE_ARRAY) {
- if (opline->opcode == ZEND_FE_FETCH_RW) {
- tmp |= 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 |= ((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_RC1 | MAY_BE_RCN;
- } else if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- }
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->op2_def);
- if (ssa_op->result_def >= 0) {
- tmp = (ssa_op->result_use >= 0) ? RES_USE_INFO() : 0;
- if (t1 & MAY_BE_OBJECT) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- 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 | MAY_BE_RCN;
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- 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:
- case ZEND_FETCH_LIST_R:
- case ZEND_FETCH_LIST_W:
- if (ssa_op->op1_def >= 0) {
- uint32_t key_type = 0;
- tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN);
- if (opline->opcode == ZEND_FETCH_DIM_W ||
- opline->opcode == ZEND_FETCH_DIM_RW ||
- opline->opcode == ZEND_FETCH_DIM_FUNC_ARG ||
- opline->opcode == ZEND_FETCH_LIST_W) {
- if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
- if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
- tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE);
- }
- tmp |= MAY_BE_ARRAY | MAY_BE_RC1;
- }
- if (t1 & (MAY_BE_STRING|MAY_BE_ARRAY)) {
- tmp |= MAY_BE_RC1;
- if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
- tmp |= t1 & MAY_BE_RCN;
- }
- }
- if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN);
- }
- if (opline->op2_type == IS_UNUSED) {
- key_type |= MAY_BE_ARRAY_KEY_LONG;
- } else {
- if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
- key_type |= MAY_BE_ARRAY_KEY_LONG;
- }
- if (t2 & MAY_BE_STRING) {
- key_type |= MAY_BE_ARRAY_KEY_STRING;
- if (opline->op2_type != IS_CONST) {
- // FIXME: numeric string
- key_type |= MAY_BE_ARRAY_KEY_LONG;
- }
- }
- if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
- key_type |= MAY_BE_ARRAY_KEY_STRING;
- }
- }
- } else if (opline->opcode == ZEND_FETCH_DIM_UNSET) {
- if (t1 & MAY_BE_ARRAY) {
- tmp |= MAY_BE_RC1;
- }
- if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN);
- }
- }
- if (opline->opcode == ZEND_FETCH_DIM_RW
- || opline->opcode == ZEND_FETCH_DIM_W
- || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
- || opline->opcode == ZEND_FETCH_LIST_W) {
- j = ssa_vars[ssa_op->result_def].use_chain;
- while (j >= 0) {
- zend_uchar opcode;
-
- if (!ssa_opcodes) {
- ZEND_ASSERT(j == (opline - op_array->opcodes) + 1 && "Use must be in next opline");
- opcode = op_array->opcodes[j].opcode;
- } else {
- ZEND_ASSERT(ssa_opcodes[j] == opline + 1 && "Use must be in next opline");
- opcode = ssa_opcodes[j]->opcode;
- }
- switch (opcode) {
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_LIST_W:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_DIM_OP:
- tmp |= key_type | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY;
- break;
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- case ZEND_SEND_REF:
- case ZEND_ASSIGN_REF:
- case ZEND_YIELD:
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- case ZEND_RETURN_BY_REF:
- case ZEND_VERIFY_RETURN_TYPE:
- case ZEND_MAKE_REF:
- case ZEND_FE_RESET_RW:
- tmp |= key_type | 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 |= key_type | MAY_BE_ARRAY_OF_DOUBLE;
- } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) {
- tmp |= key_type | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE;
- }
- break;
- case ZEND_FETCH_OBJ_W:
- case ZEND_FETCH_OBJ_RW:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- /* These will result in an error exception, unless the element
- * is already an object. */
- break;
- case ZEND_SEND_VAR:
- /* This can occur if a DIM_FETCH_FUNC_ARG with UNUSED op2 is left
- * behind, because it can't be converted to DIM_FETCH_R. */
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- j = zend_ssa_next_use(ssa->ops, ssa_op->result_def, j);
- ZEND_ASSERT(j < 0 && "There should only be one use");
- }
- }
- if ((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY)) {
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- } else {
- /* invalid key type */
- tmp = (tmp & (MAY_BE_RC1|MAY_BE_RCN)) | (t1 & ~(MAY_BE_RC1|MAY_BE_RCN));
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def);
- }
- /* FETCH_LIST on a string behaves like FETCH_R on null */
- tmp = zend_array_element_type(
- opline->opcode != ZEND_FETCH_LIST_R ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL),
- opline->op1_type,
- opline->result_type == IS_VAR,
- opline->op2_type == IS_UNUSED);
- if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) {
- tmp |= MAY_BE_NULL;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_FETCH_THIS:
- UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_op->result_def);
- UPDATE_SSA_TYPE(MAY_BE_RCN|MAY_BE_OBJECT, ssa_op->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_op->result_def >= 0) {
- zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
-
- tmp = zend_fetch_prop_type(script, prop_info, &ce);
- if (opline->result_type != IS_TMP_VAR) {
- tmp |= MAY_BE_REF | MAY_BE_INDIRECT;
- } else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) {
- zend_class_entry *ce = NULL;
-
- if (opline->op1_type == IS_UNUSED) {
- ce = op_array->scope;
- } else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) {
- ce = ssa->var_info[ssa_op->op1_use].ce;
- }
- if (prop_info) {
- /* FETCH_OBJ_R/IS for plain property increments reference counter,
- so it can't be 1 */
- if (ce && !ce->create_object) {
- tmp &= ~MAY_BE_RC1;
- }
- } else {
- if (ce && !ce->create_object && !ce->__get) {
- tmp &= ~MAY_BE_RC1;
- }
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
- }
- }
- break;
- case ZEND_FETCH_STATIC_PROP_R:
- case ZEND_FETCH_STATIC_PROP_IS:
- case ZEND_FETCH_STATIC_PROP_RW:
- case ZEND_FETCH_STATIC_PROP_W:
- case ZEND_FETCH_STATIC_PROP_UNSET:
- case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
- tmp = zend_fetch_prop_type(script,
- zend_fetch_static_prop_info(script, op_array, ssa, opline), &ce);
- if (opline->result_type != IS_TMP_VAR) {
- tmp |= MAY_BE_REF | MAY_BE_INDIRECT;
- } else {
- tmp &= ~MAY_BE_RC1;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
- }
- break;
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- if (ssa_op->result_def >= 0) {
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- zend_call_info *call_info;
-
- if (!func_info || !func_info->call_map) {
- goto unknown_opcode;
- }
- call_info = func_info->call_map[opline - op_array->opcodes];
- if (!call_info) {
- goto unknown_opcode;
- }
-
- zend_class_entry *ce;
- zend_bool ce_is_instanceof;
- tmp = zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof);
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, ce_is_instanceof, ssa_op->result_def);
- }
- }
- break;
- case ZEND_FETCH_CONSTANT:
- case ZEND_FETCH_CLASS_CONSTANT:
- UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|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_op->result_def);
- break;
- case ZEND_STRLEN:
- tmp = 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))) {
- tmp |= MAY_BE_NULL;
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- break;
- case ZEND_COUNT:
- case ZEND_FUNC_NUM_ARGS:
- UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def);
- break;
- case ZEND_FUNC_GET_ARGS:
- UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN| MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
- break;
- case ZEND_GET_CLASS:
- case ZEND_GET_CALLED_CLASS:
- UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_RCN, ssa_op->result_def);
- break;
- case ZEND_GET_TYPE:
- UPDATE_SSA_TYPE(MAY_BE_STRING|MAY_BE_RC1|MAY_BE_RCN, ssa_op->result_def);
- break;
- case ZEND_TYPE_CHECK:
- case ZEND_DEFINED:
- UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_op->result_def);
- break;
- case ZEND_VERIFY_RETURN_TYPE:
- if (t1 & MAY_BE_REF) {
- tmp = t1;
- ce = NULL;
- } else {
- zend_arg_info *ret_info = op_array->arg_info - 1;
- tmp = zend_fetch_arg_info_type(script, ret_info, &ce);
- }
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->op1_def);
- } else {
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->op1_def);
- }
- } else {
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- if (ce) {
- UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def);
- } else {
- UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
- }
- }
- break;
- case ZEND_MAKE_REF:
- tmp = 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_op->result_def);
- if (ssa_op->op1_def >= 0) {
- UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
- }
- break;
- case ZEND_CATCH:
- /* Forbidden opcodes */
- ZEND_UNREACHABLE();
- break;
- default:
-unknown_opcode:
- if (ssa_op->op1_def >= 0) {
- tmp = 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_op->op1_def);
- }
- if (ssa_op->result_def >= 0) {
- tmp = 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) {
- if (opline->opcode == ZEND_FETCH_R || opline->opcode == ZEND_FETCH_IS) {
- tmp |= MAY_BE_RCN;
- } else {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- }
- } else {
- tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
- switch (opline->opcode) {
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_FETCH_UNSET:
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_OBJ_W:
- case ZEND_FETCH_OBJ_RW:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_FETCH_OBJ_UNSET:
- case ZEND_FETCH_STATIC_PROP_W:
- case ZEND_FETCH_STATIC_PROP_RW:
- case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
- case ZEND_FETCH_STATIC_PROP_UNSET:
- tmp |= MAY_BE_INDIRECT;
- break;
- }
- }
- UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
- }
- break;
- }
-
- return SUCCESS;
-}
-
-int zend_update_type_info(
- const zend_op_array *op_array,
- zend_ssa *ssa,
- const zend_script *script,
- zend_op *opline,
- zend_ssa_op *ssa_op,
- const zend_op **ssa_opcodes,
- zend_long optimization_level)
-{
- return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level, 0);
-}
-
-static uint32_t get_class_entry_rank(zend_class_entry *ce) {
- uint32_t rank = 0;
- if (ce->ce_flags & ZEND_ACC_LINKED) {
- while (ce->parent) {
- rank++;
- ce = ce->parent;
- }
- }
- return rank;
-}
-
-/* Compute least common ancestor on class inheritance tree only */
-static zend_class_entry *join_class_entries(
- zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) {
- uint32_t rank1, rank2;
- if (ce1 == ce2) {
- return ce1;
- }
- if (!ce1 || !ce2) {
- return NULL;
- }
-
- rank1 = get_class_entry_rank(ce1);
- rank2 = get_class_entry_rank(ce2);
-
- while (rank1 != rank2) {
- if (rank1 > rank2) {
- ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent;
- rank1--;
- } else {
- ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent;
- rank2--;
- }
- }
-
- while (ce1 != ce2) {
- ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent;
- ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent;
- }
-
- if (ce1) {
- *is_instanceof = 1;
- }
- return ce1;
-}
-
-int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level)
-{
- 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;
- int i, j;
- uint32_t tmp, worklist_len = zend_bitset_len(ssa_vars_count);
- zend_bool update_worklist = 1;
-
- while (!zend_bitset_empty(worklist, worklist_len)) {
- j = zend_bitset_first(worklist, worklist_len);
- zend_bitset_excl(worklist, j);
- if (ssa_vars[j].definition_phi) {
- zend_ssa_phi *p = ssa_vars[j].definition_phi;
- if (p->pi >= 0) {
- zend_class_entry *ce = ssa_var_info[p->sources[0]].ce;
- int is_instanceof = ssa_var_info[p->sources[0]].is_instanceof;
- tmp = get_ssa_var_info(ssa, p->sources[0]);
-
- if (!p->has_range_constraint) {
- zend_ssa_type_constraint *constraint = &p->constraint.type;
- tmp &= constraint->type_mask;
- if (!(tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
- tmp &= ~(MAY_BE_RC1|MAY_BE_RCN);
- }
- if ((tmp & MAY_BE_OBJECT) && constraint->ce && ce != constraint->ce) {
- if (!ce) {
- ce = constraint->ce;
- is_instanceof = 1;
- } else if (is_instanceof && instanceof_function(constraint->ce, ce)) {
- ce = constraint->ce;
- } else {
- /* Ignore the constraint (either ce instanceof constraint->ce or
- * they are unrelated, as far as we can statically determine) */
- }
- }
- }
-
- UPDATE_SSA_TYPE(tmp, j);
- UPDATE_SSA_OBJ_TYPE(ce, 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++) {
- zend_ssa_var_info *info;
-
- ZEND_ASSERT(p->sources[i] >= 0);
- info = &ssa_var_info[p->sources[i]];
- if (info->type & MAY_BE_OBJECT) {
- if (first) {
- ce = info->ce;
- is_instanceof = info->is_instanceof;
- first = 0;
- } else {
- is_instanceof |= info->is_instanceof;
- ce = join_class_entries(ce, info->ce, &is_instanceof);
- }
- }
- }
- UPDATE_SSA_OBJ_TYPE(ce, ce ? is_instanceof : 0, j);
- }
- } else if (ssa_vars[j].definition >= 0) {
- i = ssa_vars[j].definition;
- if (_zend_update_type_info(op_array, ssa, script, worklist, op_array->opcodes + i, ssa->ops + i, NULL, optimization_level, 1) == FAILURE) {
- return FAILURE;
- }
- }
- }
- return SUCCESS;
-}
-
-static zend_bool is_narrowable_instr(zend_op *opline) {
- return opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB
- || opline->opcode == ZEND_MUL || opline->opcode == ZEND_DIV;
-}
-
-static zend_bool is_effective_op1_double_cast(zend_op *opline, zval *op2) {
- return (opline->opcode == ZEND_ADD && Z_LVAL_P(op2) == 0)
- || (opline->opcode == ZEND_SUB && Z_LVAL_P(op2) == 0)
- || (opline->opcode == ZEND_MUL && Z_LVAL_P(op2) == 1)
- || (opline->opcode == ZEND_DIV && Z_LVAL_P(op2) == 1);
-}
-static zend_bool is_effective_op2_double_cast(zend_op *opline, zval *op1) {
- /* In PHP it holds that (double)(0-$int) is bitwise identical to 0.0-(double)$int,
- * so allowing SUB here is fine. */
- return (opline->opcode == ZEND_ADD && Z_LVAL_P(op1) == 0)
- || (opline->opcode == ZEND_SUB && Z_LVAL_P(op1) == 0)
- || (opline->opcode == ZEND_MUL && Z_LVAL_P(op1) == 1);
-}
-
-/* This function recursively checks whether it's possible to convert an integer variable
- * initialization to a double initialization. The basic idea is that if the value is used
- * only in add/sub/mul/div ("narrowable" instructions) with a double result value, then it
- * will be cast to double at that point anyway, so we may as well do it earlier already.
- *
- * The tricky case are chains of operations, where it's not necessarily a given that converting
- * an integer to double before the chain of operations is the same as converting it after the
- * chain. What this function does is detect two cases where it is safe:
- * * If the operations only involve constants, then we can simply verify that performing the
- * calculation on integers and doubles yields the same value.
- * * Even if one operand is not known, we may be able to determine that the operations with the
- * integer replaced by a double only acts as an effective double cast on the unknown operand.
- * E.g. 0+$i and 0.0+$i only differ by that cast. If then the consuming instruction of this
- * result will perform a double cast anyway, the conversion is safe.
- *
- * The checks happens recursively, while keeping track of which variables are already visisted to
- * avoid infinite loops. An iterative, worklist driven approach would be possible, but the state
- * management more cumbersome to implement, so we don't bother for now.
- */
-static zend_bool can_convert_to_double(
- const zend_op_array *op_array, zend_ssa *ssa, int var_num,
- zval *value, zend_bitset visited) {
- zend_ssa_var *var = &ssa->vars[var_num];
- zend_ssa_phi *phi;
- int use;
- uint32_t type;
-
- if (zend_bitset_in(visited, var_num)) {
- return 1;
- }
- zend_bitset_incl(visited, var_num);
-
- for (use = var->use_chain; use >= 0; use = zend_ssa_next_use(ssa->ops, var_num, use)) {
- zend_op *opline = &op_array->opcodes[use];
- zend_ssa_op *ssa_op = &ssa->ops[use];
-
- if (zend_ssa_is_no_val_use(opline, ssa_op, var_num)) {
- continue;
- }
-
- if (!is_narrowable_instr(opline)) {
- return 0;
- }
-
- /* Instruction always returns double, the conversion is certainly fine */
- type = ssa->var_info[ssa_op->result_def].type;
- if ((type & MAY_BE_ANY) == MAY_BE_DOUBLE) {
- continue;
- }
-
- /* UNDEF signals that the previous result is an effective double cast, this is only allowed
- * if this instruction would have done the cast anyway (previous check). */
- if (Z_ISUNDEF_P(value)) {
- return 0;
- }
-
- /* Check that narrowing can actually be useful */
- if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
- return 0;
- }
-
- {
- /* For calculation on original values */
- zval orig_op1, orig_op2, orig_result;
- /* For calculation with var_num cast to double */
- zval dval_op1, dval_op2, dval_result;
-
- ZVAL_UNDEF(&orig_op1);
- ZVAL_UNDEF(&dval_op1);
- if (ssa_op->op1_use == var_num) {
- ZVAL_COPY_VALUE(&orig_op1, value);
- ZVAL_DOUBLE(&dval_op1, (double) Z_LVAL_P(value));
- } else if (opline->op1_type == IS_CONST) {
- zval *zv = CRT_CONSTANT(opline->op1);
- if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
- ZVAL_COPY_VALUE(&orig_op1, zv);
- ZVAL_COPY_VALUE(&dval_op1, zv);
- }
- }
-
- ZVAL_UNDEF(&orig_op2);
- ZVAL_UNDEF(&dval_op2);
- if (ssa_op->op2_use == var_num) {
- ZVAL_COPY_VALUE(&orig_op2, value);
- ZVAL_DOUBLE(&dval_op2, (double) Z_LVAL_P(value));
- } else if (opline->op2_type == IS_CONST) {
- zval *zv = CRT_CONSTANT(opline->op2);
- if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
- ZVAL_COPY_VALUE(&orig_op2, zv);
- ZVAL_COPY_VALUE(&dval_op2, zv);
- }
- }
-
- ZEND_ASSERT(!Z_ISUNDEF(orig_op1) || !Z_ISUNDEF(orig_op2));
- if (Z_ISUNDEF(orig_op1)) {
- if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op2) == 0) {
- ZVAL_LONG(&orig_result, 0);
- } else if (is_effective_op1_double_cast(opline, &orig_op2)) {
- ZVAL_UNDEF(&orig_result);
- } else {
- return 0;
- }
- } else if (Z_ISUNDEF(orig_op2)) {
- if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op1) == 0) {
- ZVAL_LONG(&orig_result, 0);
- } else if (is_effective_op2_double_cast(opline, &orig_op1)) {
- ZVAL_UNDEF(&orig_result);
- } else {
- return 0;
- }
- } else {
- zend_uchar opcode = opline->opcode;
-
- if (opcode == ZEND_ASSIGN_OP) {
- opcode = opline->extended_value;
- }
-
- /* Avoid division by zero */
- if (opcode == ZEND_DIV && zval_get_double(&orig_op2) == 0.0) {
- return 0;
- }
-
- get_binary_op(opcode)(&orig_result, &orig_op1, &orig_op2);
- get_binary_op(opcode)(&dval_result, &dval_op1, &dval_op2);
- ZEND_ASSERT(Z_TYPE(dval_result) == IS_DOUBLE);
- if (zval_get_double(&orig_result) != Z_DVAL(dval_result)) {
- return 0;
- }
- }
-
- if (!can_convert_to_double(op_array, ssa, ssa_op->result_def, &orig_result, visited)) {
- return 0;
- }
- }
- }
-
- for (phi = var->phi_use_chain; phi; phi = zend_ssa_next_use_phi(ssa, var_num, phi)) {
- /* Check that narrowing can actually be useful */
- type = ssa->var_info[phi->ssa_var].type;
- if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
- return 0;
- }
-
- if (!can_convert_to_double(op_array, ssa, phi->ssa_var, value, visited)) {
- return 0;
- }
- }
-
- return 1;
-}
-
-static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level)
-{
- uint32_t bitset_len = zend_bitset_len(ssa->vars_count);
- zend_bitset visited, worklist;
- int i, v;
- zend_op *opline;
- zend_bool narrowed = 0;
- ALLOCA_FLAG(use_heap)
-
- visited = ZEND_BITSET_ALLOCA(2 * bitset_len, use_heap);
- worklist = visited + bitset_len;
-
- zend_bitset_clear(worklist, bitset_len);
-
- for (v = op_array->last_var; v < ssa->vars_count; v++) {
- if ((ssa->var_info[v].type & (MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF)) != MAY_BE_LONG) continue;
- if (ssa->vars[v].definition < 0) continue;
- if (ssa->vars[v].no_val) continue;
- opline = op_array->opcodes + ssa->vars[v].definition;
- /* Go through assignments of literal integers and check if they can be converted to
- * doubles instead, in the hope that we'll narrow long|double to double. */
- if (opline->opcode == ZEND_ASSIGN && opline->result_type == IS_UNUSED &&
- opline->op1_type == IS_CV && opline->op2_type == IS_CONST) {
- zval *value = CRT_CONSTANT(opline->op2);
-
- zend_bitset_clear(visited, bitset_len);
- if (can_convert_to_double(op_array, ssa, v, value, visited)) {
- narrowed = 1;
- ssa->var_info[v].use_as_double = 1;
- /* The "visited" vars are exactly those which may change their type due to
- * narrowing. Reset their types and add them to the type inference worklist */
- ZEND_BITSET_FOREACH(visited, bitset_len, i) {
- ssa->var_info[i].type &= ~MAY_BE_ANY;
- } ZEND_BITSET_FOREACH_END();
- zend_bitset_union(worklist, visited, bitset_len);
- }
- }
- }
-
- if (!narrowed) {
- free_alloca(visited, use_heap);
- return SUCCESS;
- }
-
- if (zend_infer_types_ex(op_array, script, ssa, worklist, optimization_level) != SUCCESS) {
- free_alloca(visited, use_heap);
- return FAILURE;
- }
-
- free_alloca(visited, use_heap);
- 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->call_map &&
- 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->call_map[op - op_array->opcodes];
- if (call_info && op_array == &call_info->callee_func->op_array) {
- return 1;
- }
- }
- }
- return 0;
-}
-
-void zend_init_func_return_info(const zend_op_array *op_array,
- const zend_script *script,
- zend_ssa_var_info *ret)
-{
- if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_arg_info *ret_info = op_array->arg_info - 1;
- zend_ssa_range tmp_range = {0, 0, 0, 0};
-
- ret->type = zend_fetch_arg_info_type(script, ret_info, &ret->ce);
- if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
- ret->type |= MAY_BE_REF;
- }
- ret->is_instanceof = (ret->ce) ? 1 : 0;
- ret->range = tmp_range;
- ret->has_range = 0;
- }
-}
-
-void zend_func_return_info(const zend_op_array *op_array,
- const zend_script *script,
- 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_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) && blocks[j].len != 0) {
- zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
-
- if (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF) {
- zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL;
- if (!recursive && ssa_op && info->ssa.var_info &&
- ssa_op->op1_use >= 0 &&
- info->ssa.var_info[ssa_op->op1_use].recursive) {
- continue;
- }
- if (is_recursive_tail_call(op_array, opline)) {
- continue;
- }
- t1 = OP1_INFO();
- if (t1 & MAY_BE_UNDEF) {
- t1 |= MAY_BE_NULL;
- }
- if (opline->opcode == ZEND_RETURN) {
- if (t1 & MAY_BE_RC1) {
- t1 |= MAY_BE_RCN;
- }
- t1 &= ~(MAY_BE_UNDEF | MAY_BE_REF);
- } else {
- t1 |= MAY_BE_REF;
- t1 &= ~(MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN);
- }
- tmp |= t1;
-
- if (ssa_op && info->ssa.var_info &&
- ssa_op->op1_use >= 0 &&
- info->ssa.var_info[ssa_op->op1_use].ce) {
- arg_ce = info->ssa.var_info[ssa_op->op1_use].ce;
- arg_is_instanceof = info->ssa.var_info[ssa_op->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) {
- zval *zv = CRT_CONSTANT(opline->op1);
-
- if (Z_TYPE_P(zv) == 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(zv) == 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(zv) == 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(zv) == IS_LONG) {
- if (tmp_has_range < 0) {
- tmp_has_range = 1;
- tmp_range.underflow = 0;
- tmp_range.min = Z_LVAL_P(zv);
- tmp_range.max = Z_LVAL_P(zv);
- tmp_range.overflow = 0;
- } else if (tmp_has_range) {
- if (!tmp_range.underflow) {
- tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(zv));
- }
- if (!tmp_range.overflow) {
- tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(zv));
- }
- }
- } else {
- tmp_has_range = 0;
- }
- } else if (ssa_op && info->ssa.var_info && ssa_op->op1_use >= 0) {
- if (info->ssa.var_info[ssa_op->op1_use].has_range) {
- if (tmp_has_range < 0) {
- tmp_has_range = 1;
- tmp_range = info->ssa.var_info[ssa_op->op1_use].range;
- } else if (tmp_has_range) {
- /* union */
- if (info->ssa.var_info[ssa_op->op1_use].range.underflow) {
- tmp_range.underflow = 1;
- tmp_range.min = ZEND_LONG_MIN;
- } else {
- tmp_range.min = MIN(tmp_range.min, info->ssa.var_info[ssa_op->op1_use].range.min);
- }
- if (info->ssa.var_info[ssa_op->op1_use].range.overflow) {
- tmp_range.overflow = 1;
- tmp_range.max = ZEND_LONG_MAX;
- } else {
- tmp_range.max = MAX(tmp_range.max, info->ssa.var_info[ssa_op->op1_use].range.max);
- }
- }
- } else if (!widening) {
- tmp_has_range = 1;
- tmp_range.underflow = 1;
- tmp_range.min = ZEND_LONG_MIN;
- tmp_range.max = ZEND_LONG_MAX;
- tmp_range.overflow = 1;
- }
- } else {
- tmp_has_range = 0;
- }
- }
- }
- }
-
- if (!(op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
- 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_long optimization_level)
-{
- zend_ssa_var_info *ssa_var_info = ssa->var_info;
- int ssa_vars_count = ssa->vars_count;
- int j;
- zend_bitset worklist;
- ALLOCA_FLAG(use_heap);
-
- worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
- 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, optimization_level) != SUCCESS) {
- free_alloca(worklist, use_heap);
- return FAILURE;
- }
-
- /* Narrowing integer initialization to doubles */
- zend_type_narrowing(op_array, script, ssa, optimization_level);
-
- if (ZEND_FUNC_INFO(op_array)) {
- zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
- }
-
- free_alloca(worklist, use_heap);
- return SUCCESS;
-}
-
-int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) /* {{{ */
-{
- 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;
-
- if (!op_array->function_name) {
- for (i = 0; i < op_array->last_var; i++) {
- ssa_var_info[i].type = 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;
- ssa_var_info[i].has_range = 0;
- }
- } else {
- for (i = 0; i < op_array->last_var; i++) {
- ssa_var_info[i].type = MAY_BE_UNDEF;
- ssa_var_info[i].has_range = 0;
- if (ssa->vars[i].alias) {
- ssa_var_info[i].type |= get_ssa_alias_types(ssa->vars[i].alias);
- }
- }
- }
- 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, optimization_level) != 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, i;
- ALLOCA_FLAG(use_heap);
-
- if (!info->ssa.var_info || !(info->flags & ZEND_FUNC_RECURSIVE)) {
- return;
- }
- worklist_len = zend_bitset_len(info->ssa.vars_count);
- worklist = do_alloca(sizeof(zend_ulong) * worklist_len, use_heap);
- memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
- call_info = info->callee_info;
- while (call_info) {
- if (call_info->recursive && call_info->caller_call_opline &&
- 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_WORKLIST(worklist, worklist_len, i) {
- if (!info->ssa.var_info[i].recursive) {
- info->ssa.var_info[i].recursive = 1;
- add_usages(op_array, &info->ssa, worklist, i);
- }
- } WHILE_WORKLIST_END();
- free_alloca(worklist, use_heap);
-}
-
-int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2)
-{
- if (opline->op1_type == IS_CV) {
- if (t1 & MAY_BE_UNDEF) {
- switch (opline->opcode) {
- case ZEND_UNSET_VAR:
- case ZEND_ISSET_ISEMPTY_VAR:
- return 1;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_REF:
- case ZEND_BIND_GLOBAL:
- case ZEND_BIND_STATIC:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_OBJ_IS:
- case ZEND_SEND_REF:
- case ZEND_UNSET_CV:
- case ZEND_ISSET_ISEMPTY_CV:
- case ZEND_MAKE_REF:
- case ZEND_FETCH_DIM_W:
- break;
- default:
- /* undefined variable warning */
- return 1;
- }
- }
- } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
- if ((t1 & MAY_BE_RC1)
- && (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
- switch (opline->opcode) {
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- case ZEND_FETCH_LIST_R:
- case ZEND_QM_ASSIGN:
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- case ZEND_SEND_REF:
- case ZEND_SEPARATE:
- case ZEND_END_SILENCE:
- case ZEND_MAKE_REF:
- break;
- default:
- /* destructor may be called */
- return 1;
- }
- }
- }
-
- if (opline->op2_type == IS_CV) {
- if (t2 & MAY_BE_UNDEF) {
- switch (opline->opcode) {
- case ZEND_ASSIGN_REF:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- break;
- default:
- /* undefined variable warning */
- return 1;
- }
- }
- } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
- if ((t2 & MAY_BE_RC1)
- && (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- break;
- default:
- /* destructor may be called */
- return 1;
- }
- }
- }
-
- switch (opline->opcode) {
- case ZEND_NOP:
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_QM_ASSIGN:
- case ZEND_JMP:
- case ZEND_CHECK_VAR:
- case ZEND_MAKE_REF:
- case ZEND_BEGIN_SILENCE:
- case ZEND_END_SILENCE:
- case ZEND_FREE:
- case ZEND_SEPARATE:
- case ZEND_TYPE_CHECK:
- case ZEND_DEFINED:
- case ZEND_ISSET_ISEMPTY_THIS:
- case ZEND_COALESCE:
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- case ZEND_ISSET_ISEMPTY_VAR:
- case ZEND_ISSET_ISEMPTY_CV:
- case ZEND_FUNC_NUM_ARGS:
- case ZEND_FUNC_GET_ARGS:
- case ZEND_COPY_TMP:
- case ZEND_CASE_STRICT:
- case ZEND_JMP_NULL:
- return 0;
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAL:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_CHECK_FUNC_ARG:
- /* May throw for named params. */
- return opline->op2_type == IS_CONST;
- case ZEND_INIT_FCALL:
- /* can't throw, because call is resolved at compile time */
- return 0;
- case ZEND_BIND_GLOBAL:
- if ((opline+1)->opcode == ZEND_BIND_GLOBAL) {
- return zend_may_throw(opline + 1, ssa_op + 1, op_array, ssa);
- }
- return 0;
- case ZEND_ADD:
- if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY
- && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) {
- return 0;
- }
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_DIV:
- case ZEND_MOD:
- if (!OP2_HAS_RANGE() ||
- (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) {
- /* Division by zero */
- return 1;
- }
- /* break missing intentionally */
- case ZEND_SUB:
- case ZEND_MUL:
- case ZEND_POW:
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_SL:
- case ZEND_SR:
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- !OP2_HAS_RANGE() ||
- OP2_MIN_RANGE() < 0;
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
- (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- if ((t1 & MAY_BE_ANY) == MAY_BE_STRING
- && (t2 & MAY_BE_ANY) == MAY_BE_STRING) {
- return 0;
- }
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_BW_NOT:
- return (t1 & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_PRE_INC:
- case ZEND_POST_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_DEC:
- return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_BOOL_NOT:
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_BOOL:
- case ZEND_JMP_SET:
- return (t1 & MAY_BE_OBJECT);
- case ZEND_BOOL_XOR:
- return (t1 & MAY_BE_OBJECT) || (t2 & MAY_BE_OBJECT);
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_CASE:
- case ZEND_SPACESHIP:
- if ((t1 & MAY_BE_ANY) == MAY_BE_NULL
- || (t2 & MAY_BE_ANY) == MAY_BE_NULL) {
- return 0;
- }
- return (t1 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT)) || (t2 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT));
- case ZEND_ASSIGN_OP:
- if (opline->extended_value == ZEND_ADD) {
- if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY
- && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) {
- return 0;
- }
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- } else if (opline->extended_value == ZEND_DIV ||
- opline->extended_value == ZEND_MOD) {
- if (!OP2_HAS_RANGE() ||
- (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) {
- /* Division by zero */
- return 1;
- }
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- } else if (opline->extended_value == ZEND_SUB ||
- opline->extended_value == ZEND_MUL ||
- opline->extended_value == ZEND_POW) {
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- } else if (opline->extended_value == ZEND_SL ||
- opline->extended_value == ZEND_SR) {
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- !OP2_HAS_RANGE() ||
- OP2_MIN_RANGE() < 0;
- } else if (opline->extended_value == ZEND_CONCAT) {
- return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
- (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
- } else if (opline->extended_value == ZEND_BW_OR ||
- opline->extended_value == ZEND_BW_AND ||
- opline->extended_value == ZEND_BW_XOR) {
- if ((t1 & MAY_BE_ANY) == MAY_BE_STRING
- && (t2 & MAY_BE_ANY) == MAY_BE_STRING) {
- return 0;
- }
- return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
- (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- }
- return 1;
- case ZEND_ASSIGN:
- if (t1 & MAY_BE_REF) {
- return 1;
- }
- case ZEND_BIND_STATIC:
- case ZEND_UNSET_VAR:
- return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY));
- case ZEND_ASSIGN_DIM:
- if ((opline+1)->op1_type == IS_CV) {
- if (_ssa_op1_info(op_array, ssa, opline+1, ssa_op+1) & MAY_BE_UNDEF) {
- return 1;
- }
- }
- return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE)) || opline->op2_type == IS_UNUSED ||
- (t2 & (MAY_BE_UNDEF|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_ASSIGN_OBJ:
- if (t1 & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_OBJECT))) {
- return 1;
- }
- if (ssa_op->op1_use) {
- zend_ssa_var_info *var_info = ssa->var_info + ssa_op->op1_use;
- zend_class_entry *ce = var_info->ce;
-
- if (var_info->is_instanceof ||
- !ce || ce->create_object || ce->__get || ce->__set || ce->parent) {
- return 1;
- }
-
- if (op_array->scope != ce && ce->default_properties_count) {
- zend_property_info *prop_info;
-
- if (opline->op2_type == IS_CONST) {
- prop_info = zend_hash_find_ptr(&ce->properties_info,
- Z_STR_P(CRT_CONSTANT(opline->op2)));
- if (prop_info && !(prop_info->flags & ZEND_ACC_PUBLIC)) {
- return 1;
- }
- } else {
- if (t2 & (MAY_BE_ANY-MAY_BE_STRING)) {
- return 1;
- }
- ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
- if (!(prop_info->flags & ZEND_ACC_PUBLIC)) {
- return 1;
- }
- } ZEND_HASH_FOREACH_END();
- }
- }
- return 0;
- }
- return 1;
- case ZEND_ROPE_INIT:
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- return t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT);
- case ZEND_INIT_ARRAY:
- case ZEND_ADD_ARRAY_ELEMENT:
- return (opline->op2_type != IS_UNUSED) && (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_STRLEN:
- return (t1 & MAY_BE_ANY) != MAY_BE_STRING;
- case ZEND_COUNT:
- return (t1 & MAY_BE_ANY) != MAY_BE_ARRAY;
- case ZEND_RECV_INIT:
- if (Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_CONSTANT_AST) {
- return 1;
- }
- if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- uint32_t arg_num = opline->op1.num;
- zend_arg_info *cur_arg_info;
-
- if (EXPECTED(arg_num <= op_array->num_args)) {
- cur_arg_info = &op_array->arg_info[arg_num-1];
- } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
- cur_arg_info = &op_array->arg_info[op_array->num_args];
- } else {
- return 0;
- }
- return ZEND_TYPE_IS_SET(cur_arg_info->type);
- } else {
- return 0;
- }
- case ZEND_FETCH_IS:
- return (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
- case ZEND_FETCH_DIM_IS:
- return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
- case ZEND_CAST:
- switch (opline->extended_value) {
- case IS_LONG:
- case IS_DOUBLE:
- return (t1 & MAY_BE_OBJECT);
- case IS_STRING:
- return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
- case IS_ARRAY:
- return (t1 & MAY_BE_OBJECT);
- case IS_OBJECT:
- return 0;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- case ZEND_ARRAY_KEY_EXISTS:
- if ((t2 & MAY_BE_ANY) != MAY_BE_ARRAY) {
- return 1;
- }
- if ((t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
- return 1;
- }
- return 0;
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- if ((t1 & (MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) {
- return 1;
- }
- return 0;
- case ZEND_FE_FETCH_R:
- if ((t1 & (MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) {
- return 1;
- }
- if (opline->op2_type == IS_CV
- && (t2 & MAY_BE_RC1)
- && (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
- return 1;
- }
- return 0;
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_LIST_W:
- if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
- return 1;
- }
- if (t2 & (MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT)) {
- return 1;
- }
- if (opline->op2_type == IS_UNUSED) {
- return 1;
- }
- return 0;
- default:
- return 1;
- }
-}
-
-int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
-{
- return zend_may_throw_ex(opline, ssa_op, op_array, ssa, OP1_INFO(), OP2_INFO());
-}
diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h
deleted file mode 100644
index 212679df5e..0000000000
--- a/ext/opcache/Optimizer/zend_inference.h
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, e-SSA based Type & Range Inference |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#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) */
-#include "zend_type_info.h"
-
-#define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */
-#define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */
-#define MAY_BE_GUARD (1<<28) /* needs type guard */
-//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */
-
-//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
-#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */
-#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */
-
-#define MAY_HAVE_DTOR \
- (MAY_BE_OBJECT|MAY_BE_RESOURCE \
- |MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)
-
-#define DEFINE_SSA_OP_HAS_RANGE(opN) \
- static zend_always_inline zend_bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
- { \
- if (opline->opN##_type == IS_CONST) { \
- zval *zv = CRT_CONSTANT(opline->opN); \
- 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->var_info && \
- ssa_op->opN##_use >= 0 && \
- ssa->var_info[ssa_op->opN##_use].has_range); \
- } \
- return 0; \
- } \
-
-#define DEFINE_SSA_OP_MIN_RANGE(opN) \
- static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
- { \
- if (opline->opN##_type == IS_CONST) { \
- zval *zv = CRT_CONSTANT(opline->opN); \
- 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->var_info && \
- ssa_op->opN##_use >= 0 && \
- ssa->var_info[ssa_op->opN##_use].has_range) { \
- return ssa->var_info[ssa_op->opN##_use].range.min; \
- } \
- return ZEND_LONG_MIN; \
- } \
-
-#define DEFINE_SSA_OP_MAX_RANGE(opN) \
- static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
- { \
- if (opline->opN##_type == IS_CONST) { \
- zval *zv = CRT_CONSTANT(opline->opN); \
- 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->var_info && \
- ssa_op->opN##_use >= 0 && \
- ssa->var_info[ssa_op->opN##_use].has_range) { \
- return ssa->var_info[ssa_op->opN##_use].range.max; \
- } \
- return ZEND_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, const zend_ssa_op *ssa_op) \
- { \
- if (opline->opN##_type == IS_CONST) { \
- zval *zv = CRT_CONSTANT(opline->opN); \
- 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->var_info && \
- ssa_op->opN##_use >= 0 && \
- ssa->var_info[ssa_op->opN##_use].has_range) { \
- return ssa->var_info[ssa_op->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, const zend_ssa_op *ssa_op) \
- { \
- if (opline->opN##_type == IS_CONST) { \
- zval *zv = CRT_CONSTANT(opline->opN); \
- 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->var_info && \
- ssa_op->opN##_use >= 0 && \
- ssa->var_info[ssa_op->opN##_use].has_range) { \
- return ssa->var_info[ssa_op->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, ssa_op))
-#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline, ssa_op))
-#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline, ssa_op))
-#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline, ssa_op))
-#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline, ssa_op))
-#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline, ssa_op))
-#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline, ssa_op))
-#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline, ssa_op))
-#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline, ssa_op))
-#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline, ssa_op))
-
-static zend_always_inline uint32_t _const_op_type(const zval *zv) {
- if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
- return MAY_BE_RC1 | MAY_BE_RCN | 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;
- zend_string *str;
- zval *val;
-
- if (Z_REFCOUNTED_P(zv)) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- } else {
- tmp |= MAY_BE_RCN;
- }
-
- 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();
- if (HT_IS_PACKED(ht)) {
- tmp &= ~MAY_BE_ARRAY_HASH;
- }
- return tmp;
- } else {
- uint32_t tmp = (1 << Z_TYPE_P(zv));
-
- if (Z_REFCOUNTED_P(zv)) {
- tmp |= MAY_BE_RC1 | MAY_BE_RCN;
- } else if (Z_TYPE_P(zv) == IS_STRING) {
- tmp |= MAY_BE_RCN;
- }
- return tmp;
- }
-}
-
-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_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_INDIRECT | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
-}
-
-#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, const zend_ssa_op *ssa_op) \
- { \
- if (opline->opN##_type == IS_CONST) { \
- return _const_op_type(CRT_CONSTANT(opline->opN)); \
- } else { \
- return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->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, const zend_ssa_op *ssa_op) \
- { \
- return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->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, ssa_op))
-#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op))
-#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
-#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
-#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op))
-#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op))
-#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op))
-#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
-#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
-#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op))
-
-static zend_always_inline zend_bool zend_add_will_overflow(zend_long a, zend_long b) {
- return (b > 0 && a > ZEND_LONG_MAX - b)
- || (b < 0 && a < ZEND_LONG_MIN - b);
-}
-static zend_always_inline zend_bool zend_sub_will_overflow(zend_long a, zend_long b) {
- return (b > 0 && a < ZEND_LONG_MIN + b)
- || (b < 0 && a > ZEND_LONG_MAX + b);
-}
-
-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, zend_long optimization_level);
-
-uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, 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);
-int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp);
-void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_bool underflow, zend_long min, zend_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, zend_long optimization_level);
-
-uint32_t zend_fetch_arg_info_type(
- const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce);
-void zend_init_func_return_info(const zend_op_array *op_array,
- const zend_script *script,
- zend_ssa_var_info *ret);
-void zend_func_return_info(const zend_op_array *op_array,
- const zend_script *script,
- int recursive,
- int widening,
- zend_ssa_var_info *ret);
-
-int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2);
-int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa);
-
-int zend_update_type_info(const zend_op_array *op_array,
- zend_ssa *ssa,
- const zend_script *script,
- zend_op *opline,
- zend_ssa_op *ssa_op,
- const zend_op **ssa_opcodes,
- zend_long optimization_level);
-
-END_EXTERN_C()
-
-#endif /* ZEND_INFERENCE_H */
diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c
deleted file mode 100644
index c80992ed8d..0000000000
--- a/ext/opcache/Optimizer/zend_optimizer.c
+++ /dev/null
@@ -1,1568 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "Optimizer/zend_optimizer.h"
-#include "Optimizer/zend_optimizer_internal.h"
-#include "zend_API.h"
-#include "zend_constants.h"
-#include "zend_execute.h"
-#include "zend_vm.h"
-#include "zend_cfg.h"
-#include "zend_func_info.h"
-#include "zend_call_graph.h"
-#include "zend_inference.h"
-#include "zend_dump.h"
-
-static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
-{
- zval_ptr_dtor_nogc(zvalue);
-}
-
-void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
-{
- zval val;
-
- if (!ctx->constants) {
- ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
- zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
- }
- ZVAL_COPY(&val, value);
- zend_hash_add(ctx->constants, Z_STR_P(name), &val);
-}
-
-int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2) /* {{{ */
-{
- binary_op_type binary_op = get_binary_op(opcode);
- int er, ret;
-
- if (zend_binary_op_produces_error(opcode, op1, op2)) {
- return FAILURE;
- }
-
- er = EG(error_reporting);
- EG(error_reporting) = 0;
- ret = binary_op(result, op1, op2);
- EG(error_reporting) = er;
-
- return ret;
-}
-/* }}} */
-
-int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1) /* {{{ */
-{
- unary_op_type unary_op = get_unary_op(opcode);
-
- if (unary_op) {
- if (opcode == ZEND_BW_NOT
- && Z_TYPE_P(op1) != IS_LONG
- && Z_TYPE_P(op1) != IS_DOUBLE
- && Z_TYPE_P(op1) != IS_STRING) {
- /* produces "Unsupported operand types" exception */
- return FAILURE;
- }
- return unary_op(result, op1);
- } else { /* ZEND_BOOL */
- ZVAL_BOOL(result, zend_is_true(op1));
- return SUCCESS;
- }
-}
-/* }}} */
-
-int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */
-{
- switch (type) {
- case IS_NULL:
- ZVAL_NULL(result);
- return SUCCESS;
- case _IS_BOOL:
- ZVAL_BOOL(result, zval_is_true(op1));
- return SUCCESS;
- case IS_LONG:
- ZVAL_LONG(result, zval_get_long(op1));
- return SUCCESS;
- case IS_DOUBLE:
- ZVAL_DOUBLE(result, zval_get_double(op1));
- return SUCCESS;
- case IS_STRING:
- /* Conversion from double to string takes into account run-time
- 'precision' setting and cannot be evaluated at compile-time */
- if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
- ZVAL_STR(result, zval_get_string(op1));
- return SUCCESS;
- }
- break;
- case IS_ARRAY:
- ZVAL_COPY(result, op1);
- convert_to_array(result);
- return SUCCESS;
- }
- return FAILURE;
-}
-/* }}} */
-
-int zend_optimizer_eval_strlen(zval *result, zval *op1) /* {{{ */
-{
- if (Z_TYPE_P(op1) != IS_STRING) {
- return FAILURE;
- }
- ZVAL_LONG(result, Z_STRLEN_P(op1));
- return SUCCESS;
-}
-/* }}} */
-
-int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
-{
- zval *val;
-
- if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
- ZVAL_COPY(value, val);
- return 1;
- }
- return 0;
-}
-
-int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
-{
- int i = op_array->last_literal;
- op_array->last_literal++;
- op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
- ZVAL_COPY_VALUE(&op_array->literals[i], zv);
- Z_EXTRA(op_array->literals[i]) = 0;
- return i;
-}
-
-static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
- zval zv;
- ZVAL_STR(&zv, str);
- zend_string_hash_val(str);
- return zend_optimizer_add_literal(op_array, &zv);
-}
-
-static inline void drop_leading_backslash(zval *val) {
- if (Z_STRVAL_P(val)[0] == '\\') {
- zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
- zval_ptr_dtor_nogc(val);
- ZVAL_STR(val, str);
- }
-}
-
-static inline uint32_t alloc_cache_slots(zend_op_array *op_array, uint32_t num) {
- uint32_t ret = op_array->cache_size;
- op_array->cache_size += num * sizeof(void *);
- return ret;
-}
-
-#define REQUIRES_STRING(val) do { \
- if (Z_TYPE_P(val) != IS_STRING) { \
- return 0; \
- } \
-} while (0)
-
-#define TO_STRING_NOWARN(val) do { \
- if (Z_TYPE_P(val) >= IS_ARRAY) { \
- return 0; \
- } \
- convert_to_string(val); \
-} while (0)
-
-int zend_optimizer_update_op1_const(zend_op_array *op_array,
- zend_op *opline,
- zval *val)
-{
- switch (opline->opcode) {
- case ZEND_OP_DATA:
- switch ((opline-1)->opcode) {
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- return 0;
- }
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- break;
- case ZEND_FREE:
- case ZEND_CHECK_VAR:
- MAKE_NOP(opline);
- zval_ptr_dtor_nogc(val);
- return 1;
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_W:
- case ZEND_ASSIGN_DIM:
- case ZEND_RETURN_BY_REF:
- case ZEND_INSTANCEOF:
- case ZEND_MAKE_REF:
- return 0;
- case ZEND_CATCH:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_LAST_CATCH);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- break;
- case ZEND_DEFINED:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- opline->extended_value = alloc_cache_slots(op_array, 1);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- break;
- case ZEND_NEW:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- opline->op2.num = alloc_cache_slots(op_array, 1);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- break;
- case ZEND_INIT_STATIC_METHOD_CALL:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- if (opline->op2_type != IS_CONST) {
- opline->result.num = alloc_cache_slots(op_array, 1);
- }
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- break;
- case ZEND_FETCH_CLASS_CONSTANT:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- if (opline->op2_type != IS_CONST) {
- opline->extended_value = alloc_cache_slots(op_array, 1);
- }
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- break;
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- break;
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- case ZEND_FETCH_STATIC_PROP_R:
- case ZEND_FETCH_STATIC_PROP_W:
- case ZEND_FETCH_STATIC_PROP_RW:
- case ZEND_FETCH_STATIC_PROP_IS:
- case ZEND_FETCH_STATIC_PROP_UNSET:
- case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
- case ZEND_UNSET_STATIC_PROP:
- case ZEND_ISSET_ISEMPTY_STATIC_PROP:
- case ZEND_PRE_INC_STATIC_PROP:
- case ZEND_PRE_DEC_STATIC_PROP:
- case ZEND_POST_INC_STATIC_PROP:
- case ZEND_POST_DEC_STATIC_PROP:
- TO_STRING_NOWARN(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) == op_array->cache_size) {
- op_array->cache_size += sizeof(void *);
- } else {
- opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
- }
- break;
- case ZEND_SEND_VAR:
- opline->opcode = ZEND_SEND_VAL;
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- break;
- case ZEND_SEPARATE:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- return 0;
- case ZEND_VERIFY_RETURN_TYPE:
- /* This would require a non-local change.
- * zend_optimizer_replace_by_const() supports this. */
- return 0;
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- case ZEND_FETCH_LIST_R:
- case ZEND_COPY_TMP:
- case ZEND_FETCH_CLASS_NAME:
- return 0;
- case ZEND_ECHO:
- {
- zval zv;
- if (Z_TYPE_P(val) != IS_STRING && zend_optimizer_eval_cast(&zv, IS_STRING, val) == SUCCESS) {
- zval_ptr_dtor_nogc(val);
- val = &zv;
- }
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- if (Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val) == 0) {
- MAKE_NOP(opline);
- }
- /* TODO: In a subsequent pass, *after* this step and compacting nops, combine consecutive ZEND_ECHOs using the block information from ssa->cfg */
- /* (e.g. for ext/opcache/tests/opt/sccp_010.phpt) */
- break;
- }
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- case ZEND_FETCH_FUNC_ARG:
- TO_STRING_NOWARN(val);
- if (opline->opcode == ZEND_CONCAT && opline->op2_type == IS_CONST) {
- opline->opcode = ZEND_FAST_CONCAT;
- }
- /* break missing intentionally */
- default:
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- break;
- }
-
- opline->op1_type = IS_CONST;
- if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
- zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
- }
- return 1;
-}
-
-int zend_optimizer_update_op2_const(zend_op_array *op_array,
- zend_op *opline,
- zval *val)
-{
- zval tmp;
-
- switch (opline->opcode) {
- case ZEND_ASSIGN_REF:
- case ZEND_FAST_CALL:
- return 0;
- case ZEND_FETCH_CLASS:
- if ((opline + 1)->opcode == ZEND_INSTANCEOF &&
- (opline + 1)->op2.var == opline->result.var) {
- return 0;
- }
- /* break missing intentionally */
- case ZEND_INSTANCEOF:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- opline->extended_value = alloc_cache_slots(op_array, 1);
- break;
- case ZEND_INIT_FCALL_BY_NAME:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- opline->result.num = alloc_cache_slots(op_array, 1);
- break;
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- case ZEND_FETCH_STATIC_PROP_R:
- case ZEND_FETCH_STATIC_PROP_W:
- case ZEND_FETCH_STATIC_PROP_RW:
- case ZEND_FETCH_STATIC_PROP_IS:
- case ZEND_FETCH_STATIC_PROP_UNSET:
- case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
- case ZEND_UNSET_STATIC_PROP:
- case ZEND_ISSET_ISEMPTY_STATIC_PROP:
- case ZEND_PRE_INC_STATIC_PROP:
- case ZEND_PRE_DEC_STATIC_PROP:
- case ZEND_POST_INC_STATIC_PROP:
- case ZEND_POST_DEC_STATIC_PROP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- REQUIRES_STRING(val);
- drop_leading_backslash(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- if (opline->op1_type != IS_CONST) {
- opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & (ZEND_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS));
- }
- break;
- case ZEND_INIT_FCALL:
- REQUIRES_STRING(val);
- if (Z_REFCOUNT_P(val) == 1) {
- zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
- } else {
- ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val)));
- zval_ptr_dtor_nogc(val);
- val = &tmp;
- }
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- opline->result.num = alloc_cache_slots(op_array, 1);
- break;
- case ZEND_INIT_DYNAMIC_CALL:
- if (Z_TYPE_P(val) == IS_STRING) {
- if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
- return 0;
- }
-
- if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
- /* Dynamic call to various special functions must stay dynamic,
- * otherwise would drop a warning */
- return 0;
- }
-
- opline->opcode = ZEND_INIT_FCALL_BY_NAME;
- drop_leading_backslash(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- opline->result.num = alloc_cache_slots(op_array, 1);
- } else {
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- }
- break;
- case ZEND_INIT_METHOD_CALL:
- REQUIRES_STRING(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- opline->result.num = alloc_cache_slots(op_array, 2);
- break;
- case ZEND_INIT_STATIC_METHOD_CALL:
- REQUIRES_STRING(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
- if (opline->op1_type != IS_CONST) {
- opline->result.num = alloc_cache_slots(op_array, 2);
- }
- break;
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_OBJ_W:
- case ZEND_FETCH_OBJ_RW:
- case ZEND_FETCH_OBJ_IS:
- case ZEND_FETCH_OBJ_UNSET:
- case ZEND_FETCH_OBJ_FUNC_ARG:
- case ZEND_UNSET_OBJ:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- case ZEND_ASSIGN_OBJ_OP:
- TO_STRING_NOWARN(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- opline->extended_value = alloc_cache_slots(op_array, 3);
- break;
- case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- TO_STRING_NOWARN(val);
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_ISEMPTY);
- break;
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- case ZEND_ASSIGN_DIM:
- case ZEND_UNSET_DIM:
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_R:
- case ZEND_FETCH_LIST_W:
- if (Z_TYPE_P(val) == IS_STRING) {
- zend_ulong index;
-
- if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
- ZVAL_LONG(&tmp, index);
- opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
- zend_string_hash_val(Z_STR_P(val));
- zend_optimizer_add_literal(op_array, val);
- Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE;
- break;
- }
- }
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- break;
- case ZEND_ADD_ARRAY_ELEMENT:
- case ZEND_INIT_ARRAY:
- if (Z_TYPE_P(val) == IS_STRING) {
- zend_ulong index;
- if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
- zval_ptr_dtor_nogc(val);
- ZVAL_LONG(val, index);
- }
- }
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- break;
- case ZEND_ROPE_INIT:
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- TO_STRING_NOWARN(val);
- if (opline->opcode == ZEND_CONCAT && opline->op1_type == IS_CONST) {
- opline->opcode = ZEND_FAST_CONCAT;
- }
- /* break missing intentionally */
- default:
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- break;
- }
-
- opline->op2_type = IS_CONST;
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
- zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
- }
- return 1;
-}
-
-int zend_optimizer_replace_by_const(zend_op_array *op_array,
- zend_op *opline,
- zend_uchar type,
- uint32_t var,
- zval *val)
-{
- zend_op *end = op_array->opcodes + op_array->last;
-
- while (opline < end) {
- if (opline->op1_type == type &&
- opline->op1.var == var) {
- switch (opline->opcode) {
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_W:
- case ZEND_ASSIGN_DIM:
- case ZEND_SEPARATE:
- case ZEND_RETURN_BY_REF:
- return 0;
- case ZEND_SEND_VAR:
- opline->extended_value = 0;
- opline->opcode = ZEND_SEND_VAL;
- break;
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- opline->extended_value = 0;
- opline->opcode = ZEND_SEND_VAL_EX;
- break;
- case ZEND_SEND_VAR_NO_REF:
- return 0;
- case ZEND_SEND_VAR_NO_REF_EX:
- opline->opcode = ZEND_SEND_VAL;
- break;
- case ZEND_SEND_USER:
- opline->opcode = ZEND_SEND_VAL_EX;
- break;
- /* In most cases IS_TMP_VAR operand may be used only once.
- * The operands are usually destroyed by the opcode handler.
- * ZEND_CASE[_STRICT] and ZEND_FETCH_LIST_R are exceptions, they keeps operand
- * unchanged, and allows its reuse. these instructions
- * usually terminated by ZEND_FREE that finally kills the value.
- */
- case ZEND_FETCH_LIST_R: {
- zend_op *m = opline;
-
- do {
- if (m->opcode == ZEND_FETCH_LIST_R &&
- m->op1_type == type &&
- m->op1.var == var) {
- zval v;
- ZVAL_COPY(&v, val);
- if (Z_TYPE(v) == IS_STRING) {
- zend_string_hash_val(Z_STR(v));
- }
- m->op1.constant = zend_optimizer_add_literal(op_array, &v);
- m->op1_type = IS_CONST;
- }
- m++;
- } while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var);
-
- ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
- MAKE_NOP(m);
- zval_ptr_dtor_nogc(val);
- return 1;
- }
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- case ZEND_CASE:
- case ZEND_CASE_STRICT: {
- zend_op *end = op_array->opcodes + op_array->last;
- while (opline < end) {
- if (opline->op1_type == type && opline->op1.var == var) {
- if (
- opline->opcode == ZEND_CASE
- || opline->opcode == ZEND_CASE_STRICT
- || opline->opcode == ZEND_SWITCH_LONG
- || opline->opcode == ZEND_SWITCH_STRING
- || opline->opcode == ZEND_MATCH
- ) {
- zval v;
-
- if (opline->opcode == ZEND_CASE) {
- opline->opcode = ZEND_IS_EQUAL;
- } else if (opline->opcode == ZEND_CASE_STRICT) {
- opline->opcode = ZEND_IS_IDENTICAL;
- }
- ZVAL_COPY(&v, val);
- if (Z_TYPE(v) == IS_STRING) {
- zend_string_hash_val(Z_STR(v));
- }
- opline->op1.constant = zend_optimizer_add_literal(op_array, &v);
- opline->op1_type = IS_CONST;
- } else if (opline->opcode == ZEND_FREE) {
- if (opline->extended_value == ZEND_FREE_SWITCH) {
- /* We found the end of the switch. */
- MAKE_NOP(opline);
- break;
- }
-
- ZEND_ASSERT(opline->extended_value == ZEND_FREE_ON_RETURN);
- MAKE_NOP(opline);
- } else {
- ZEND_UNREACHABLE();
- }
- }
- opline++;
- }
- zval_ptr_dtor_nogc(val);
- return 1;
- }
- case ZEND_VERIFY_RETURN_TYPE: {
- zend_arg_info *ret_info = op_array->arg_info - 1;
- if (!ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val))
- || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
- return 0;
- }
- MAKE_NOP(opline);
-
- /* zend_handle_loops_and_finally may inserts other oplines */
- do {
- ++opline;
- } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
- ZEND_ASSERT(opline->op1.var == var);
-
- break;
- }
- default:
- break;
- }
- return zend_optimizer_update_op1_const(op_array, opline, val);
- }
-
- if (opline->op2_type == type &&
- opline->op2.var == var) {
- return zend_optimizer_update_op2_const(op_array, opline, val);
- }
- opline++;
- }
-
- return 1;
-}
-
-/* Update jump offsets after a jump was migrated to another opline */
-void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
- switch (new_opline->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
- break;
- case ZEND_JMPZNZ:
- new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
- /* break missing intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
- }
- break;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- {
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
- zval *zv;
- ZEND_HASH_FOREACH_VAL(jumptable, zv) {
- Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
- } ZEND_HASH_FOREACH_END();
- new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
- break;
- }
- }
-}
-
-/* Shift jump offsets based on shiftlist */
-void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
- switch (opline->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
- break;
- case ZEND_JMPZNZ:
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
- /* break missing intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
- break;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- {
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
- zval *zv;
- ZEND_HASH_FOREACH_VAL(jumptable, zv) {
- Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]);
- } ZEND_HASH_FOREACH_END();
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
- break;
- }
- }
-}
-
-static zend_class_entry *get_class_entry_from_op1(
- zend_script *script, zend_op_array *op_array, zend_op *opline) {
- if (opline->op1_type == IS_CONST) {
- zval *op1 = CRT_CONSTANT(opline->op1);
- if (Z_TYPE_P(op1) == IS_STRING) {
- zend_string *class_name = Z_STR_P(op1 + 1);
- zend_class_entry *ce;
- if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
- return ce;
- } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
- if (ce->type == ZEND_INTERNAL_CLASS) {
- return ce;
- } else if (ce->type == ZEND_USER_CLASS &&
- ce->info.user.filename &&
- ce->info.user.filename == op_array->filename) {
- return ce;
- }
- }
- }
- } else if (opline->op1_type == IS_UNUSED && op_array->scope
- && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
- && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- return op_array->scope;
- }
- return NULL;
-}
-
-zend_function *zend_optimizer_get_called_func(
- zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool *is_prototype)
-{
- *is_prototype = 0;
- switch (opline->opcode) {
- case ZEND_INIT_FCALL:
- {
- zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2));
- zend_function *func;
- if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
- return func;
- } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
- if (func->type == ZEND_INTERNAL_FUNCTION) {
- return func;
- } else if (func->type == ZEND_USER_FUNCTION &&
- func->op_array.filename &&
- func->op_array.filename == op_array->filename) {
- return func;
- }
- }
- break;
- }
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
- zval *function_name = CRT_CONSTANT(opline->op2) + 1;
- zend_function *func;
- if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
- return func;
- } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
- if (func->type == ZEND_INTERNAL_FUNCTION) {
- return func;
- } else if (func->type == ZEND_USER_FUNCTION &&
- func->op_array.filename &&
- func->op_array.filename == op_array->filename) {
- return func;
- }
- }
- }
- break;
- case ZEND_INIT_STATIC_METHOD_CALL:
- if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
- zend_class_entry *ce = get_class_entry_from_op1(
- script, op_array, opline);
- if (ce) {
- zend_string *func_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1);
- zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name);
- if (fbc) {
- zend_bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0;
- zend_bool same_scope = fbc->common.scope == op_array->scope;
- if (is_public || same_scope) {
- return fbc;
- }
- }
- }
- }
- break;
- case ZEND_INIT_METHOD_CALL:
- if (opline->op1_type == IS_UNUSED
- && opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING
- && op_array->scope
- && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)
- && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
- zend_string *method_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1);
- zend_function *fbc = zend_hash_find_ptr(
- &op_array->scope->function_table, method_name);
- if (fbc) {
- zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
- zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
- zend_bool same_scope = fbc->common.scope == op_array->scope;
- if (is_private) {
- /* Only use private method if in the same scope. We can't even use it
- * as a prototype, as it may be overridden with changed signature. */
- return same_scope ? fbc : NULL;
- }
- /* If the method is non-final, it may be overridden,
- * but only with a compatible method signature. */
- *is_prototype = !is_final;
- return fbc;
- }
- }
- break;
- case ZEND_NEW:
- {
- zend_class_entry *ce = get_class_entry_from_op1(
- script, op_array, opline);
- if (ce && ce->type == ZEND_USER_CLASS) {
- return ce->constructor;
- }
- break;
- }
- }
- return NULL;
-}
-
-uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
- if (zend_string_equals_literal(name, "extract")) {
- return ZEND_FUNC_INDIRECT_VAR_ACCESS;
- } else if (zend_string_equals_literal(name, "compact")) {
- return ZEND_FUNC_INDIRECT_VAR_ACCESS;
- } else if (zend_string_equals_literal(name, "get_defined_vars")) {
- return ZEND_FUNC_INDIRECT_VAR_ACCESS;
- } else if (zend_string_equals_literal(name, "db2_execute")) {
- return ZEND_FUNC_INDIRECT_VAR_ACCESS;
- } else if (zend_string_equals_literal(name, "func_num_args")) {
- return ZEND_FUNC_VARARG;
- } else if (zend_string_equals_literal(name, "func_get_arg")) {
- return ZEND_FUNC_VARARG;
- } else if (zend_string_equals_literal(name, "func_get_args")) {
- return ZEND_FUNC_VARARG;
- } else {
- return 0;
- }
-}
-
-zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) {
- uint32_t var = free_opline->op1.var;
- ZEND_ASSERT(zend_optimizer_is_loop_var_free(free_opline));
-
- while (--free_opline >= op_array->opcodes) {
- if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) {
- return free_opline;
- }
- }
- return NULL;
-}
-
-static void zend_optimize(zend_op_array *op_array,
- zend_optimizer_ctx *ctx)
-{
- if (op_array->type == ZEND_EVAL_CODE) {
- return;
- }
-
- if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
- zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL);
- }
-
- /* pass 1 (Simple local optimizations)
- * - persistent constant substitution (true, false, null, etc)
- * - constant casting (ADD expects numbers, CONCAT strings, etc)
- * - constant expression evaluation
- * - optimize constant conditional JMPs
- * - pre-evaluate constant function calls
- * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM
- */
- 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, 0, "after pass 1", NULL);
- }
- }
-
- /* pass 3: (Jump optimization)
- * - optimize series of JMPs
- */
- if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
- zend_optimizer_pass3(op_array, ctx);
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
- zend_dump_op_array(op_array, 0, "after pass 3", NULL);
- }
- }
-
- /* pass 4:
- * - INIT_FCALL_BY_NAME -> DO_FCALL
- */
- if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
- zend_optimize_func_calls(op_array, ctx);
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
- zend_dump_op_array(op_array, 0, "after pass 4", NULL);
- }
- }
-
- /* pass 5:
- * - CFG optimization
- */
- if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
- zend_optimize_cfg(op_array, ctx);
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
- zend_dump_op_array(op_array, 0, "after pass 5", NULL);
- }
- }
-
- /* pass 6:
- * - DFA optimization
- */
- if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
- !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
- zend_optimize_dfa(op_array, ctx);
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
- zend_dump_op_array(op_array, 0, "after pass 6", NULL);
- }
- }
-
- /* pass 9:
- * - Optimize temp variables usage
- */
- if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
- zend_optimize_temporary_variables(op_array, ctx);
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
- zend_dump_op_array(op_array, 0, "after pass 9", NULL);
- }
- }
-
- /* pass 10:
- * - remove NOPs
- */
- if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
- zend_optimizer_nop_removal(op_array, ctx);
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
- zend_dump_op_array(op_array, 0, "after pass 10", NULL);
- }
- }
-
- /* pass 11:
- * - Compact literals table
- */
- if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) &&
- (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
- !(ZEND_OPTIMIZER_PASS_7 & 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, 0, "after pass 11", NULL);
- }
- }
-
- if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) &&
- (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
- !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
- zend_optimizer_compact_vars(op_array);
- if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) {
- zend_dump_op_array(op_array, 0, "after pass 13", NULL);
- }
- }
-
- if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) {
- return;
- }
-
- if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
- zend_dump_op_array(op_array, 0, "after optimizer", NULL);
- }
-}
-
-static void zend_revert_pass_two(zend_op_array *op_array)
-{
- zend_op *opline, *end;
-
- ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0);
-
- opline = op_array->opcodes;
- end = opline + op_array->last;
- while (opline < end) {
- if (opline->op1_type == IS_CONST) {
- ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1);
- }
- if (opline->op2_type == IS_CONST) {
- ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op2);
- }
- /* reset smart branch flags IS_SMART_BRANCH_JMP[N]Z */
- opline->result_type &= (IS_TMP_VAR|IS_VAR|IS_CV|IS_CONST);
- opline++;
- }
-#if !ZEND_USE_ABS_CONST_ADDR
- if (op_array->literals) {
- zval *literals = emalloc(sizeof(zval) * op_array->last_literal);
- memcpy(literals, op_array->literals, sizeof(zval) * op_array->last_literal);
- op_array->literals = literals;
- }
-#endif
-
- op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO;
-}
-
-static void zend_redo_pass_two(zend_op_array *op_array)
-{
- zend_op *opline, *end;
-#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
- zend_op *old_opcodes = op_array->opcodes;
-#endif
-
- ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0);
-
-#if !ZEND_USE_ABS_CONST_ADDR
- if (op_array->last_literal) {
- op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
- ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
- sizeof(zval) * op_array->last_literal);
- memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
- op_array->literals, sizeof(zval) * op_array->last_literal);
- efree(op_array->literals);
- op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
- } else {
- if (op_array->literals) {
- efree(op_array->literals);
- }
- op_array->literals = NULL;
- }
-#endif
-
- opline = op_array->opcodes;
- end = opline + op_array->last;
- while (opline < end) {
- if (opline->op1_type == IS_CONST) {
- ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
- }
- if (opline->op2_type == IS_CONST) {
- ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
- }
- /* fix jumps to point to new array */
- switch (opline->opcode) {
-#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
- break;
- case ZEND_JMPZNZ:
- /* relative extended_value don't have to be changed */
- /* break omitted intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- /* relative extended_value don't have to be changed */
- break;
-#endif
- 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_CASE:
- case ZEND_CASE_STRICT:
- case ZEND_ISSET_ISEMPTY_CV:
- 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_INSTANCEOF:
- case ZEND_TYPE_CHECK:
- case ZEND_DEFINED:
- case ZEND_IN_ARRAY:
- case ZEND_ARRAY_KEY_EXISTS:
- if (opline->result_type & IS_TMP_VAR) {
- /* reinitialize result_type of smart branch instructions */
- if (opline + 1 < end) {
- if ((opline+1)->opcode == ZEND_JMPZ
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR;
- } else if ((opline+1)->opcode == ZEND_JMPNZ
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR;
- }
- }
- }
- break;
- }
- ZEND_VM_SET_OPCODE_HANDLER(opline);
- opline++;
- }
-
- op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
-}
-
-static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
-{
- zend_op *opline, *end;
-#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
- zend_op *old_opcodes = op_array->opcodes;
-#endif
-
- ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0);
-
-#if !ZEND_USE_ABS_CONST_ADDR
- if (op_array->last_literal) {
- op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
- ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
- sizeof(zval) * op_array->last_literal);
- memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
- op_array->literals, sizeof(zval) * op_array->last_literal);
- efree(op_array->literals);
- op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
- } else {
- if (op_array->literals) {
- efree(op_array->literals);
- }
- op_array->literals = NULL;
- }
-#endif
-
- opline = op_array->opcodes;
- end = opline + op_array->last;
- while (opline < end) {
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
- uint32_t op1_info = opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
- uint32_t op2_info = opline->op1_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
- uint32_t res_info =
- (opline->opcode == ZEND_PRE_INC ||
- opline->opcode == ZEND_PRE_DEC ||
- opline->opcode == ZEND_POST_INC ||
- opline->opcode == ZEND_POST_DEC) ?
- ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
- (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)));
-
- if (opline->op1_type == IS_CONST) {
- ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
- }
- if (opline->op2_type == IS_CONST) {
- ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
- }
-
- /* fix jumps to point to new array */
- switch (opline->opcode) {
-#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
- break;
- case ZEND_JMPZNZ:
- /* relative extended_value don't have to be changed */
- /* break omitted intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_ASSERT_CHECK:
- case ZEND_JMP_NULL:
- opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
- break;
- case ZEND_CATCH:
- if (!(opline->extended_value & ZEND_LAST_CATCH)) {
- opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- /* relative extended_value don't have to be changed */
- break;
-#endif
- 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_CASE:
- case ZEND_CASE_STRICT:
- case ZEND_ISSET_ISEMPTY_CV:
- 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_INSTANCEOF:
- case ZEND_TYPE_CHECK:
- case ZEND_DEFINED:
- case ZEND_IN_ARRAY:
- case ZEND_ARRAY_KEY_EXISTS:
- if (opline->result_type & IS_TMP_VAR) {
- /* reinitialize result_type of smart branch instructions */
- if (opline + 1 < end) {
- if ((opline+1)->opcode == ZEND_JMPZ
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR;
- } else if ((opline+1)->opcode == ZEND_JMPNZ
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR;
- }
- }
- }
- break;
- }
- zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info);
- opline++;
- }
-
- op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
-}
-
-static void zend_optimize_op_array(zend_op_array *op_array,
- zend_optimizer_ctx *ctx)
-{
- /* Revert pass_two() */
- zend_revert_pass_two(op_array);
-
- /* Do actual optimizations */
- zend_optimize(op_array, ctx);
-
- /* Redo pass_two() */
- zend_redo_pass_two(op_array);
-
- if (op_array->live_range) {
- zend_recalc_live_ranges(op_array, NULL);
- }
-}
-
-static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
-{
- zend_function *func;
- zend_op *opline, *end;
-
- opline = op_array->opcodes;
- end = opline + op_array->last;
- while (opline < end) {
- if (opline->opcode == ZEND_INIT_FCALL) {
- func = zend_hash_find_ptr(
- &ctx->script->function_table,
- Z_STR_P(RT_CONSTANT(opline, opline->op2)));
- if (func) {
- opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
- }
- }
- opline++;
- }
-}
-
-static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
-{
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
-
- if (func_info) {
- zend_call_info *call_info =func_info->callee_info;
-
- while (call_info) {
- zend_op *opline = call_info->caller_init_opline;
-
- if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
- opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
- }
- call_info = call_info->next_callee;
- }
- }
-}
-
-static zend_bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) {
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes];
- int ssa_var = ssa_op->result_def;
- if (ssa_var < 0) {
- /* Be conservative. */
- return 1;
- }
-
- /* If the variable is used by a PHI, this may be the assignment of the final branch of a
- * ternary/etc structure. While this is where the live range starts, the value from the other
- * branch may also be used. As such, use the type of the PHI node for the following check. */
- if (func_info->ssa.vars[ssa_var].phi_use_chain) {
- ssa_var = func_info->ssa.vars[ssa_var].phi_use_chain->ssa_var;
- }
-
- uint32_t type = func_info->ssa.var_info[ssa_var].type;
- return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
-}
-
-void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
-{
- zend_class_entry *ce;
- zend_string *key;
- zend_op_array *op_array;
-
- func(&script->main_op_array, context);
-
- ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
- func(op_array, context);
- } ZEND_HASH_FOREACH_END();
-
- ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
- if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
- continue;
- }
- ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
- if (op_array->scope == ce
- && op_array->type == ZEND_USER_FUNCTION
- && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
- func(op_array, context);
- }
- } ZEND_HASH_FOREACH_END();
- } ZEND_HASH_FOREACH_END();
-}
-
-static void step_optimize_op_array(zend_op_array *op_array, void *context) {
- zend_optimize_op_array(op_array, (zend_optimizer_ctx *) context);
-}
-
-static void step_adjust_fcall_stack_size(zend_op_array *op_array, void *context) {
- zend_adjust_fcall_stack_size(op_array, (zend_optimizer_ctx *) context);
-}
-
-static void step_dump_after_optimizer(zend_op_array *op_array, void *context) {
- zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL);
-}
-
-int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
-{
- zend_class_entry *ce;
- zend_string *key;
- zend_op_array *op_array;
- zend_string *name;
- zend_optimizer_ctx ctx;
- zend_call_graph call_graph;
-
- ctx.arena = zend_arena_create(64 * 1024);
- ctx.script = script;
- ctx.constants = NULL;
- ctx.optimization_level = optimization_level;
- ctx.debug_level = debug_level;
-
- if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
- (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
- zend_build_call_graph(&ctx.arena, script, &call_graph) == SUCCESS) {
- /* Optimize using call-graph */
- int i;
- zend_func_info *func_info;
-
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- zend_revert_pass_two(call_graph.op_arrays[i]);
- zend_optimize(call_graph.op_arrays[i], &ctx);
- }
-
- zend_analyze_call_graph(&ctx.arena, script, &call_graph);
-
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (func_info) {
- func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
- if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
- }
- }
- }
-
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (func_info) {
- if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) {
- func_info->flags = func_info->ssa.cfg.flags;
- } else {
- ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
- }
- }
- }
-
- //TODO: perform inner-script inference???
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (func_info) {
- zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map);
- }
- }
-
- if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
- }
- }
-
- if (ZEND_OPTIMIZER_PASS_11 & optimization_level) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx);
- if (debug_level & ZEND_DUMP_AFTER_PASS_11) {
- zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL);
- }
- }
- }
-
- if (ZEND_OPTIMIZER_PASS_13 & optimization_level) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- zend_optimizer_compact_vars(call_graph.op_arrays[i]);
- if (debug_level & ZEND_DUMP_AFTER_PASS_13) {
- zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL);
- }
- }
- }
-
- if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
- }
- }
-
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- op_array = call_graph.op_arrays[i];
- func_info = ZEND_FUNC_INFO(op_array);
- if (func_info && func_info->ssa.var_info) {
- zend_redo_pass_two_ex(op_array, &func_info->ssa);
- if (op_array->live_range) {
- zend_recalc_live_ranges(op_array, needs_live_range);
- }
- } else {
- zend_redo_pass_two(op_array);
- if (op_array->live_range) {
- zend_recalc_live_ranges(op_array, NULL);
- }
- }
- }
-
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
- }
- } else {
- zend_foreach_op_array(script, step_optimize_op_array, &ctx);
-
- if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
- zend_foreach_op_array(script, step_adjust_fcall_stack_size, &ctx);
- }
- }
-
- ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
- if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
- continue;
- }
- ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
- if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) {
- zend_op_array *orig_op_array =
- zend_hash_find_ptr(&op_array->scope->function_table, name);
-
- ZEND_ASSERT(orig_op_array != NULL);
- if (orig_op_array != op_array) {
- uint32_t fn_flags = op_array->fn_flags;
- zend_function *prototype = op_array->prototype;
- HashTable *ht = op_array->static_variables;
-
- *op_array = *orig_op_array;
- op_array->fn_flags = fn_flags;
- op_array->prototype = prototype;
- op_array->static_variables = ht;
- }
- }
- } ZEND_HASH_FOREACH_END();
- } ZEND_HASH_FOREACH_END();
-
- if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) &&
- (ZEND_OPTIMIZER_PASS_7 & optimization_level)) {
- zend_foreach_op_array(script, step_dump_after_optimizer, NULL);
- }
-
- if (ctx.constants) {
- zend_hash_destroy(ctx.constants);
- }
- zend_arena_destroy(ctx.arena);
-
- return 1;
-}
-
-int zend_optimizer_startup(void)
-{
- return zend_func_info_startup();
-}
-
-int zend_optimizer_shutdown(void)
-{
- return zend_func_info_shutdown();
-}
diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h
deleted file mode 100644
index 04528d33e2..0000000000
--- a/ext/opcache/Optimizer/zend_optimizer.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef ZEND_OPTIMIZER_H
-#define ZEND_OPTIMIZER_H
-
-#include "zend.h"
-#include "zend_compile.h"
-
-#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* Simple local optimizations */
-#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* */
-#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* Jump optimization */
-#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */
-#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
-#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
-#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
-#define ZEND_OPTIMIZER_PASS_8 (1<<7) /* SCCP (constant propagation) */
-#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
-#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
-#define ZEND_OPTIMIZER_PASS_11 (1<<10) /* Merge equal constants */
-#define ZEND_OPTIMIZER_PASS_12 (1<<11) /* Adjust used stack */
-#define ZEND_OPTIMIZER_PASS_13 (1<<12) /* Remove unused variables */
-#define ZEND_OPTIMIZER_PASS_14 (1<<13) /* DCE (dead code elimination) */
-#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* (unsafe) Collect constants */
-#define ZEND_OPTIMIZER_PASS_16 (1<<15) /* Inline functions */
-
-#define ZEND_OPTIMIZER_IGNORE_OVERLOADING (1<<16) /* (unsafe) Ignore possibility of operator overloading */
-
-#define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF
-
-#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEBFFF"
-
-
-#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1
-#define ZEND_DUMP_AFTER_PASS_2 ZEND_OPTIMIZER_PASS_2
-#define ZEND_DUMP_AFTER_PASS_3 ZEND_OPTIMIZER_PASS_3
-#define ZEND_DUMP_AFTER_PASS_4 ZEND_OPTIMIZER_PASS_4
-#define ZEND_DUMP_AFTER_PASS_5 ZEND_OPTIMIZER_PASS_5
-#define ZEND_DUMP_AFTER_PASS_6 ZEND_OPTIMIZER_PASS_6
-#define ZEND_DUMP_AFTER_PASS_7 ZEND_OPTIMIZER_PASS_7
-#define ZEND_DUMP_AFTER_PASS_8 ZEND_OPTIMIZER_PASS_8
-#define ZEND_DUMP_AFTER_PASS_9 ZEND_OPTIMIZER_PASS_9
-#define ZEND_DUMP_AFTER_PASS_10 ZEND_OPTIMIZER_PASS_10
-#define ZEND_DUMP_AFTER_PASS_11 ZEND_OPTIMIZER_PASS_11
-#define ZEND_DUMP_AFTER_PASS_12 ZEND_OPTIMIZER_PASS_12
-#define ZEND_DUMP_AFTER_PASS_13 ZEND_OPTIMIZER_PASS_13
-#define ZEND_DUMP_AFTER_PASS_14 ZEND_OPTIMIZER_PASS_14
-
-#define ZEND_DUMP_BEFORE_OPTIMIZER (1<<16)
-#define ZEND_DUMP_AFTER_OPTIMIZER (1<<17)
-
-#define ZEND_DUMP_BEFORE_BLOCK_PASS (1<<18)
-#define ZEND_DUMP_AFTER_BLOCK_PASS (1<<19)
-#define ZEND_DUMP_BLOCK_PASS_VARS (1<<20)
-
-#define ZEND_DUMP_BEFORE_DFA_PASS (1<<21)
-#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)
-#define ZEND_DUMP_SCCP (1<<29)
-
-typedef struct _zend_script {
- zend_string *filename;
- zend_op_array main_op_array;
- HashTable function_table;
- HashTable class_table;
- uint32_t first_early_binding_opline; /* the linked list of delayed declarations */
-} 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_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h
deleted file mode 100644
index 0c8c2fe4c7..0000000000
--- a/ext/opcache/Optimizer/zend_optimizer_internal.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend OPcache |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andi Gutmans <andi@php.net> |
- | Zeev Suraski <zeev@php.net> |
- | Stanislav Malyshev <stas@zend.com> |
- | Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef ZEND_OPTIMIZER_INTERNAL_H
-#define ZEND_OPTIMIZER_INTERNAL_H
-
-#include "zend_ssa.h"
-#include "zend_func_info.h"
-
-#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
-#define ZEND_OP1_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op1)
-#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
-#define ZEND_OP2_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op2)
-
-#define VAR_NUM(v) EX_VAR_TO_NUM(v)
-#define NUM_VAR(v) EX_NUM_TO_VAR(v)
-
-#define INV_COND(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ : ZEND_JMPZ)
-#define INV_EX_COND(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ : ZEND_JMPZ)
-#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
-#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
-
-#define RESULT_UNUSED(op) (op->result_type == IS_UNUSED)
-#define SAME_VAR(op1, op2) (op1 ## _type == op2 ## _type && op1.var == op2.var)
-
-typedef struct _zend_optimizer_ctx {
- zend_arena *arena;
- zend_script *script;
- HashTable *constants;
- zend_long optimization_level;
- zend_long debug_level;
-} zend_optimizer_ctx;
-
-#define LITERAL_LONG(op, val) do { \
- zval _c; \
- ZVAL_LONG(&_c, val); \
- op.constant = zend_optimizer_add_literal(op_array, &_c); \
- } while (0)
-
-#define LITERAL_BOOL(op, val) do { \
- zval _c; \
- ZVAL_BOOL(&_c, val); \
- op.constant = zend_optimizer_add_literal(op_array, &_c); \
- } while (0)
-
-#define literal_dtor(zv) do { \
- zval_ptr_dtor_nogc(zv); \
- ZVAL_NULL(zv); \
- } while (0)
-
-#define COPY_NODE(target, src) do { \
- target ## _type = src ## _type; \
- target = src; \
- } while (0)
-
-static inline zend_bool zend_optimizer_is_loop_var_free(const zend_op *opline) {
- return (opline->opcode == ZEND_FE_FREE && opline->extended_value != ZEND_FREE_ON_RETURN)
- || (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH);
-}
-
-int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv);
-int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy);
-void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value);
-int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value);
-int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2);
-int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1);
-int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1);
-int zend_optimizer_eval_strlen(zval *result, zval *op1);
-int zend_optimizer_update_op1_const(zend_op_array *op_array,
- zend_op *opline,
- zval *val);
-int zend_optimizer_update_op2_const(zend_op_array *op_array,
- zend_op *opline,
- zval *val);
-int zend_optimizer_replace_by_const(zend_op_array *op_array,
- zend_op *opline,
- zend_uchar type,
- uint32_t var,
- zval *val);
-zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline);
-
-void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa);
-void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map);
-void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void zend_optimizer_compact_vars(zend_op_array *op_array);
-zend_function *zend_optimizer_get_called_func(
- zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool *is_prototype);
-uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
-void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
-void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist);
-int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_arrya, zend_ssa *ssa, zend_call_info **call_map);
-int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects);
-int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa);
-
-typedef void (*zend_op_array_func_t)(zend_op_array *, void *context);
-void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context);
-
-#endif
diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c
deleted file mode 100644
index 3a6a098196..0000000000
--- a/ext/opcache/Optimizer/zend_ssa.c
+++ /dev/null
@@ -1,1628 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, SSA - Static Single Assignment Form |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- | Nikita Popov <nikic@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#include "php.h"
-#include "zend_compile.h"
-#include "zend_dfg.h"
-#include "zend_ssa.h"
-#include "zend_dump.h"
-#include "zend_inference.h"
-#include "Optimizer/zend_optimizer_internal.h"
-
-static zend_bool dominates(const zend_basic_block *blocks, int a, int b) {
- while (blocks[b].level > blocks[a].level) {
- b = blocks[b].idom;
- }
- return a == b;
-}
-
-static zend_bool will_rejoin(
- const zend_cfg *cfg, const zend_dfg *dfg, const zend_basic_block *block,
- int other_successor, int exclude, int var) {
- int i;
- for (i = 0; i < block->predecessors_count; i++) {
- int predecessor = cfg->predecessors[block->predecessor_offset + i];
- if (predecessor == exclude) {
- continue;
- }
-
- /* The variable is changed in this predecessor,
- * so we will not rejoin with the original value. */
- // TODO: This should not be limited to the direct predecessor block.
- if (DFG_ISSET(dfg->def, dfg->size, predecessor, var)) {
- continue;
- }
-
- /* The other successor dominates this predecessor,
- * so we will get the original value from it. */
- if (dominates(cfg->blocks, other_successor, predecessor)) {
- return 1;
- }
- }
- return 0;
-}
-
-static zend_bool needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */
-{
- zend_basic_block *from_block, *to_block;
- int other_successor;
-
- if (!DFG_ISSET(dfg->in, dfg->size, to, var)) {
- /* Variable is not live, certainly won't benefit from pi */
- return 0;
- }
-
- /* Make sure that both successors of the from block aren't the same. Pi nodes are associated
- * with predecessor blocks, so we can't distinguish which edge the pi belongs to. */
- from_block = &ssa->cfg.blocks[from];
- ZEND_ASSERT(from_block->successors_count == 2);
- if (from_block->successors[0] == from_block->successors[1]) {
- return 0;
- }
-
- to_block = &ssa->cfg.blocks[to];
- if (to_block->predecessors_count == 1) {
- /* Always place pi if one predecessor (an if branch) */
- return 1;
- }
-
- /* Check whether we will rejoin with the original value coming from the other successor,
- * in which case the pi node will not have an effect. */
- other_successor = from_block->successors[0] == to
- ? from_block->successors[1] : from_block->successors[0];
- return !will_rejoin(&ssa->cfg, dfg, to_block, other_successor, from, var);
-}
-/* }}} */
-
-static zend_ssa_phi *add_pi(
- zend_arena **arena, const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa,
- int from, int to, int var) /* {{{ */
-{
- zend_ssa_phi *phi;
- if (!needs_pi(op_array, dfg, ssa, from, to, var)) {
- return NULL;
- }
-
- phi = zend_arena_calloc(arena, 1,
- ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
- ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count) +
- sizeof(void*) * ssa->cfg.blocks[to].predecessors_count);
- phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
- memset(phi->sources, 0xff, sizeof(int) * ssa->cfg.blocks[to].predecessors_count);
- phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count));
-
- phi->pi = from;
- phi->var = var;
- phi->ssa_var = -1;
- phi->next = ssa->blocks[to].phis;
- ssa->blocks[to].phis = phi;
-
- /* Block "to" now defines "var" via the pi statement, so add it to the "def" set. Note that
- * this is not entirely accurate, because the pi is actually placed along the edge from->to.
- * If there is a back-edge to "to" this may result in non-minimal SSA form. */
- DFG_SET(dfg->def, dfg->size, to, var);
-
- /* If there are multiple predecessors in the target block, we need to place a phi there.
- * However this can (generally) not be expressed in terms of dominance frontiers, so place it
- * explicitly. dfg->use here really is dfg->phi, we're reusing the set. */
- if (ssa->cfg.blocks[to].predecessors_count > 1) {
- DFG_SET(dfg->use, dfg->size, to, var);
- }
-
- return phi;
-}
-/* }}} */
-
-static void pi_range(
- zend_ssa_phi *phi, int min_var, int max_var, zend_long min, zend_long max,
- char underflow, char overflow, char negative) /* {{{ */
-{
- zend_ssa_range_constraint *constraint = &phi->constraint.range;
- constraint->min_var = min_var;
- constraint->max_var = max_var;
- constraint->min_ssa_var = -1;
- constraint->max_ssa_var = -1;
- constraint->range.min = min;
- constraint->range.max = max;
- constraint->range.underflow = underflow;
- constraint->range.overflow = overflow;
- constraint->negative = negative ? NEG_INIT : NEG_NONE;
- phi->has_range_constraint = 1;
-}
-/* }}} */
-
-static inline void pi_range_equals(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, var, var, val, val, 0, 0, 0);
-}
-static inline void pi_range_not_equals(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, var, var, val, val, 0, 0, 1);
-}
-static inline void pi_range_min(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, var, -1, val, ZEND_LONG_MAX, 0, 1, 0);
-}
-static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, -1, var, ZEND_LONG_MIN, val, 1, 0, 0);
-}
-
-static void pi_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
- phi->has_range_constraint = 0;
- phi->constraint.type.ce = NULL;
- phi->constraint.type.type_mask = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
- phi->constraint.type.type_mask |= type_mask;
- if (type_mask & MAY_BE_NULL) {
- phi->constraint.type.type_mask |= MAY_BE_UNDEF;
- }
-}
-static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
- uint32_t relevant = MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- pi_type_mask(phi, ~type_mask & relevant);
-}
-static inline uint32_t mask_for_type_check(uint32_t type) {
- if (type & MAY_BE_ARRAY) {
- return type | (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
- } else {
- return type;
- }
-}
-
-/* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand.
- * This negated adjustment is what is written into the "adjustment" parameter. */
-static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment) /* {{{ */
-{
- zend_op *op = opline;
- zval *zv;
-
- while (op != op_array->opcodes) {
- op--;
- if (op->result_type != IS_TMP_VAR || op->result.var != var_num) {
- continue;
- }
-
- if (op->opcode == ZEND_POST_DEC) {
- if (op->op1_type == IS_CV) {
- *adjustment = -1;
- return EX_VAR_TO_NUM(op->op1.var);
- }
- } else if (op->opcode == ZEND_POST_INC) {
- if (op->op1_type == IS_CV) {
- *adjustment = 1;
- return EX_VAR_TO_NUM(op->op1.var);
- }
- } else if (op->opcode == ZEND_ADD) {
- if (op->op1_type == IS_CV && op->op2_type == IS_CONST) {
- zv = CRT_CONSTANT_EX(op_array, op, op->op2);
- if (Z_TYPE_P(zv) == IS_LONG
- && Z_LVAL_P(zv) != ZEND_LONG_MIN) {
- *adjustment = -Z_LVAL_P(zv);
- return EX_VAR_TO_NUM(op->op1.var);
- }
- } else if (op->op2_type == IS_CV && op->op1_type == IS_CONST) {
- zv = CRT_CONSTANT_EX(op_array, op, op->op1);
- if (Z_TYPE_P(zv) == IS_LONG
- && Z_LVAL_P(zv) != ZEND_LONG_MIN) {
- *adjustment = -Z_LVAL_P(zv);
- return EX_VAR_TO_NUM(op->op2.var);
- }
- }
- } else if (op->opcode == ZEND_SUB) {
- if (op->op1_type == IS_CV && op->op2_type == IS_CONST) {
- zv = CRT_CONSTANT_EX(op_array, op, op->op2);
- if (Z_TYPE_P(zv) == IS_LONG) {
- *adjustment = Z_LVAL_P(zv);
- return EX_VAR_TO_NUM(op->op1.var);
- }
- }
- }
- break;
- }
- return -1;
-}
-/* }}} */
-
-/* e-SSA construction: Pi placement (Pi is actually a Phi with single
- * source and constraint).
- * Order of Phis is important, Pis must be placed before Phis
- */
-static void place_essa_pis(
- zend_arena **arena, const zend_script *script, const zend_op_array *op_array,
- uint32_t build_flags, zend_ssa *ssa, zend_dfg *dfg) /* {{{ */ {
- zend_basic_block *blocks = ssa->cfg.blocks;
- int j, blocks_count = ssa->cfg.blocks_count;
- for (j = 0; j < blocks_count; j++) {
- zend_ssa_phi *pi;
- zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
- int bt; /* successor block number if a condition is true */
- int bf; /* successor block number if a condition is false */
-
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0 || blocks[j].len == 0) {
- continue;
- }
- /* the last instruction of basic block is conditional branch,
- * based on comparison of CV(s)
- */
- switch (opline->opcode) {
- case ZEND_JMPZ:
- case ZEND_JMPZNZ:
- bf = blocks[j].successors[0];
- bt = blocks[j].successors[1];
- break;
- case ZEND_JMPNZ:
- bt = blocks[j].successors[0];
- bf = blocks[j].successors[1];
- break;
- case ZEND_COALESCE:
- if (opline->op1_type == IS_CV) {
- int var = EX_VAR_TO_NUM(opline->op1.var);
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, blocks[j].successors[0], var))) {
- pi_not_type_mask(pi, MAY_BE_NULL);
- }
- }
- continue;
- case ZEND_JMP_NULL:
- if (opline->op1_type == IS_CV) {
- int var = EX_VAR_TO_NUM(opline->op1.var);
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, blocks[j].successors[1], var))) {
- pi_not_type_mask(pi, MAY_BE_NULL);
- }
- }
- continue;
- default:
- continue;
- }
- if (opline->op1_type == IS_TMP_VAR &&
- ((opline-1)->opcode == ZEND_IS_EQUAL ||
- (opline-1)->opcode == ZEND_IS_NOT_EQUAL ||
- (opline-1)->opcode == ZEND_IS_SMALLER ||
- (opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) &&
- opline->op1.var == (opline-1)->result.var) {
- int var1 = -1;
- int var2 = -1;
- zend_long val1 = 0;
- zend_long val2 = 0;
-// long val = 0;
-
- if ((opline-1)->op1_type == IS_CV) {
- var1 = EX_VAR_TO_NUM((opline-1)->op1.var);
- } else if ((opline-1)->op1_type == IS_TMP_VAR) {
- var1 = find_adjusted_tmp_var(
- op_array, build_flags, opline, (opline-1)->op1.var, &val2);
- }
-
- if ((opline-1)->op2_type == IS_CV) {
- var2 = EX_VAR_TO_NUM((opline-1)->op2.var);
- } else if ((opline-1)->op2_type == IS_TMP_VAR) {
- var2 = find_adjusted_tmp_var(
- op_array, build_flags, opline, (opline-1)->op2.var, &val1);
- }
-
- if (var1 >= 0 && var2 >= 0) {
- if (!zend_sub_will_overflow(val1, val2) && !zend_sub_will_overflow(val2, val1)) {
- zend_long tmp = val1;
- val1 -= val2;
- val2 -= tmp;
- } else {
- var1 = -1;
- var2 = -1;
- }
- } else if (var1 >= 0 && var2 < 0) {
- zend_long add_val2 = 0;
- if ((opline-1)->op2_type == IS_CONST) {
- zval *zv = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2);
-
- if (Z_TYPE_P(zv) == IS_LONG) {
- add_val2 = Z_LVAL_P(zv);
- } else if (Z_TYPE_P(zv) == IS_FALSE) {
- add_val2 = 0;
- } else if (Z_TYPE_P(zv) == IS_TRUE) {
- add_val2 = 1;
- } else {
- var1 = -1;
- }
- } else {
- var1 = -1;
- }
- if (!zend_add_will_overflow(val2, add_val2)) {
- val2 += add_val2;
- } else {
- var1 = -1;
- }
- } else if (var1 < 0 && var2 >= 0) {
- zend_long add_val1 = 0;
- if ((opline-1)->op1_type == IS_CONST) {
- zval *zv = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1);
- if (Z_TYPE_P(zv) == IS_LONG) {
- add_val1 = Z_LVAL_P(CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1));
- } else if (Z_TYPE_P(zv) == IS_FALSE) {
- add_val1 = 0;
- } else if (Z_TYPE_P(zv) == IS_TRUE) {
- add_val1 = 1;
- } else {
- var2 = -1;
- }
- } else {
- var2 = -1;
- }
- if (!zend_add_will_overflow(val1, add_val1)) {
- val1 += add_val1;
- } else {
- var2 = -1;
- }
- }
-
- if (var1 >= 0) {
- if ((opline-1)->opcode == ZEND_IS_EQUAL) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
- pi_range_equals(pi, var2, val2);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
- pi_range_not_equals(pi, var2, val2);
- }
- } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
- pi_range_equals(pi, var2, val2);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
- pi_range_not_equals(pi, var2, val2);
- }
- } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
- if (val2 > ZEND_LONG_MIN) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
- pi_range_max(pi, var2, val2-1);
- }
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
- pi_range_min(pi, var2, val2);
- }
- } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
- pi_range_max(pi, var2, val2);
- }
- if (val2 < ZEND_LONG_MAX) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
- pi_range_min(pi, var2, val2+1);
- }
- }
- }
- }
- if (var2 >= 0) {
- if((opline-1)->opcode == ZEND_IS_EQUAL) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
- pi_range_equals(pi, var1, val1);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
- pi_range_not_equals(pi, var1, val1);
- }
- } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
- pi_range_equals(pi, var1, val1);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
- pi_range_not_equals(pi, var1, val1);
- }
- } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
- if (val1 < ZEND_LONG_MAX) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
- pi_range_min(pi, var1, val1+1);
- }
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
- pi_range_max(pi, var1, val1);
- }
- } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
- pi_range_min(pi, var1, val1);
- }
- if (val1 > ZEND_LONG_MIN) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
- pi_range_max(pi, var1, val1-1);
- }
- }
- }
- }
- } else if (opline->op1_type == IS_TMP_VAR &&
- ((opline-1)->opcode == ZEND_POST_INC ||
- (opline-1)->opcode == ZEND_POST_DEC) &&
- opline->op1.var == (opline-1)->result.var &&
- (opline-1)->op1_type == IS_CV) {
- int var = EX_VAR_TO_NUM((opline-1)->op1.var);
-
- if ((opline-1)->opcode == ZEND_POST_DEC) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
- pi_range_equals(pi, -1, -1);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
- pi_range_not_equals(pi, -1, -1);
- }
- } else if ((opline-1)->opcode == ZEND_POST_INC) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
- pi_range_equals(pi, -1, 1);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
- pi_range_not_equals(pi, -1, 1);
- }
- }
- } else if (opline->op1_type == IS_TMP_VAR &&
- ((opline-1)->opcode == ZEND_PRE_INC ||
- (opline-1)->opcode == ZEND_PRE_DEC) &&
- opline->op1.var == (opline-1)->result.var &&
- (opline-1)->op1_type == IS_CV) {
- int var = EX_VAR_TO_NUM((opline-1)->op1.var);
-
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
- pi_range_equals(pi, -1, 0);
- }
- /* speculative */
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
- pi_range_not_equals(pi, -1, 0);
- }
- } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK &&
- opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) {
- int var = EX_VAR_TO_NUM((opline-1)->op1.var);
- uint32_t type = (opline-1)->extended_value;
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
- pi_type_mask(pi, mask_for_type_check(type));
- }
- if (type != MAY_BE_RESOURCE) {
- /* is_resource() may return false for closed resources */
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
- pi_not_type_mask(pi, mask_for_type_check(type));
- }
- }
- } else if (opline->op1_type == IS_TMP_VAR &&
- ((opline-1)->opcode == ZEND_IS_IDENTICAL
- || (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) &&
- opline->op1.var == (opline-1)->result.var) {
- int var;
- zval *val;
- uint32_t type_mask;
- if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) {
- var = EX_VAR_TO_NUM((opline-1)->op1.var);
- val = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2);
- } else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) {
- var = EX_VAR_TO_NUM((opline-1)->op2.var);
- val = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1);
- } else {
- continue;
- }
-
- /* We're interested in === null/true/false comparisons here, because they eliminate
- * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */
- if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) {
- continue;
- }
-
- type_mask = _const_op_type(val);
- if ((opline-1)->opcode == ZEND_IS_IDENTICAL) {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
- pi_type_mask(pi, type_mask);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
- pi_not_type_mask(pi, type_mask);
- }
- } else {
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
- pi_type_mask(pi, type_mask);
- }
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
- pi_not_type_mask(pi, type_mask);
- }
- }
- } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_INSTANCEOF &&
- opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV &&
- (opline-1)->op2_type == IS_CONST) {
- int var = EX_VAR_TO_NUM((opline-1)->op1.var);
- zend_string *lcname = Z_STR_P(CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2) + 1);
- zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
- if (!ce) {
- ce = zend_hash_find_ptr(CG(class_table), lcname);
- if (!ce || ce->type != ZEND_INTERNAL_CLASS) {
- continue;
- }
- }
-
- if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
- pi_type_mask(pi, MAY_BE_OBJECT);
- pi->constraint.type.ce = ce;
- }
- }
- }
-}
-/* }}} */
-
-static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */
-{
- const zend_op *next;
-
- if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)];
- //USE_SSA_VAR(op_array->last_var + opline->op1.var)
- }
- if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
- //USE_SSA_VAR(op_array->last_var + opline->op2.var)
- }
- if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
- && opline->result_type == IS_CV
- && opline->opcode != ZEND_RECV) {
- ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
- //USE_SSA_VAR(op_array->last_var + opline->result.var)
- }
-
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
- ssa_ops[k].op2_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(opline->op2.var)
- }
- if (opline->op1_type == IS_CV) {
-add_op1_def:
- ssa_ops[k].op1_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(opline->op1.var)
- }
- break;
- case ZEND_ASSIGN_REF:
- if (opline->op2_type == IS_CV) {
- ssa_ops[k].op2_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(opline->op2.var)
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
- //USE_SSA_VAR(op_array->last_var + next->op1.var);
- if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
- ssa_ops[k + 1].op1_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(next->op1.var)
- }
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_OBJ_REF:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
- //USE_SSA_VAR(op_array->last_var + next->op1.var);
- if (next->op1_type == IS_CV) {
- ssa_ops[k + 1].op1_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(next->op1.var)
- }
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
- //USE_SSA_VAR(op_array->last_var + next->op1.var);
- if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
- ssa_ops[k + 1].op1_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(next->op1.var)
- }
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP_REF:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
- //USE_SSA_VAR(op_array->last_var + next->op1.var);
- if (next->op1_type == IS_CV) {
- ssa_ops[k + 1].op1_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(next->op1.var)
- }
- }
- break;
- case ZEND_ASSIGN_STATIC_PROP_OP:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
- //USE_SSA_VAR(op_array->last_var + next->op1.var);
- }
- break;
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- next = opline + 1;
- if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
- //USE_SSA_VAR(op_array->last_var + next->op1.var);
- }
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ASSIGN_OP:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- case ZEND_BIND_GLOBAL:
- case ZEND_BIND_STATIC:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_REF:
- case ZEND_SEND_UNPACK:
- case ZEND_FE_RESET_RW:
- case ZEND_MAKE_REF:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- case ZEND_UNSET_DIM:
- case ZEND_UNSET_OBJ:
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- case ZEND_FETCH_DIM_FUNC_ARG:
- case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_W:
- if (opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_SEND_VAR:
- case ZEND_CAST:
- case ZEND_QM_ASSIGN:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_FE_RESET_R:
- if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_ADD_ARRAY_UNPACK:
- ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
- break;
- case ZEND_ADD_ARRAY_ELEMENT:
- ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
- /* break missing intentionally */
- case ZEND_INIT_ARRAY:
- if (((build_flags & ZEND_SSA_RC_INFERENCE)
- || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
- && opline->op1_type == IS_CV) {
- goto add_op1_def;
- }
- break;
- case ZEND_YIELD:
- if (opline->op1_type == IS_CV
- && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
- || (build_flags & ZEND_SSA_RC_INFERENCE))) {
- goto add_op1_def;
- }
- break;
- case ZEND_UNSET_CV:
- goto add_op1_def;
- case ZEND_VERIFY_RETURN_TYPE:
- if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
- goto add_op1_def;
- }
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- if (opline->op2_type != IS_CV) {
- ssa_ops[k].op2_use = -1; /* not used */
- }
- ssa_ops[k].op2_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(opline->op2.var)
- break;
- case ZEND_BIND_LEXICAL:
- if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
- ssa_ops[k].op2_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(opline->op2.var)
- }
- break;
- default:
- break;
- }
-
- if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
- ssa_ops[k].result_def = ssa_vars_count;
- var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
- ssa_vars_count++;
- //NEW_SSA_VAR(op_array->last_var + opline->result.var)
- }
-
- return ssa_vars_count;
-}
-/* }}} */
-
-int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */
-{
- return _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var);
-}
-/* }}} */
-
-static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */
-{
- 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;
- int i, j;
- zend_op *opline, *end;
- int *tmp = NULL;
- ALLOCA_FLAG(use_heap = 0);
-
- // FIXME: Can we optimize this copying out in some cases?
- if (blocks[n].next_child >= 0) {
- tmp = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), use_heap);
- memcpy(tmp, var, sizeof(int) * (op_array->last_var + op_array->T));
- var = tmp;
- }
-
- if (ssa_blocks[n].phis) {
- zend_ssa_phi *phi = ssa_blocks[n].phis;
- do {
- if (phi->ssa_var < 0) {
- phi->ssa_var = ssa_vars_count;
- var[phi->var] = ssa_vars_count;
- ssa_vars_count++;
- } else {
- var[phi->var] = phi->ssa_var;
- }
- phi = phi->next;
- } while (phi);
- }
-
- opline = op_array->opcodes + blocks[n].start;
- end = opline + blocks[n].len;
- for (; opline < end; opline++) {
- uint32_t k = opline - op_array->opcodes;
- if (opline->opcode != ZEND_OP_DATA) {
- ssa_vars_count = _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var);
- }
- }
-
- zend_ssa_op *fe_fetch_ssa_op = blocks[n].len != 0
- && ((end-1)->opcode == ZEND_FE_FETCH_R || (end-1)->opcode == ZEND_FE_FETCH_RW)
- && (end-1)->op2_type == IS_CV
- ? &ssa_ops[blocks[n].start + blocks[n].len - 1] : NULL;
- for (i = 0; i < blocks[n].successors_count; i++) {
- int succ = blocks[n].successors[i];
- zend_ssa_phi *p;
- for (p = ssa_blocks[succ].phis; p; p = p->next) {
- if (p->pi == n) {
- /* e-SSA Pi */
- if (p->has_range_constraint) {
- if (p->constraint.range.min_var >= 0) {
- p->constraint.range.min_ssa_var = var[p->constraint.range.min_var];
- }
- if (p->constraint.range.max_var >= 0) {
- p->constraint.range.max_ssa_var = var[p->constraint.range.max_var];
- }
- }
- for (j = 0; j < blocks[succ].predecessors_count; j++) {
- p->sources[j] = var[p->var];
- }
- if (p->ssa_var < 0) {
- p->ssa_var = ssa_vars_count;
- ssa_vars_count++;
- }
- } else if (p->pi < 0) {
- /* Normal Phi */
- for (j = 0; j < blocks[succ].predecessors_count; j++)
- if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
- break;
- }
- ZEND_ASSERT(j < blocks[succ].predecessors_count);
- p->sources[j] = var[p->var];
- if (fe_fetch_ssa_op && i == 0 && p->sources[j] == fe_fetch_ssa_op->op2_def) {
- /* On the exit edge of an FE_FETCH, use the pre-modification value instead. */
- p->sources[j] = fe_fetch_ssa_op->op2_use;
- }
- }
- }
- for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) {
- if (p->pi == n) {
- zend_ssa_phi *q = p->next;
- while (q) {
- if (q->pi < 0 && q->var == p->var) {
- for (j = 0; j < blocks[succ].predecessors_count; j++) {
- if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
- break;
- }
- }
- ZEND_ASSERT(j < blocks[succ].predecessors_count);
- q->sources[j] = p->ssa_var;
- }
- q = q->next;
- }
- }
- }
- }
-
- ssa->vars_count = ssa_vars_count;
-
- j = blocks[n].children;
- while (j >= 0) {
- // FIXME: Tail call optimization?
- if (zend_ssa_rename(op_array, build_flags, ssa, var, j) != SUCCESS)
- return FAILURE;
- j = blocks[j].next_child;
- }
-
- if (tmp) {
- free_alloca(tmp, use_heap);
- }
-
- return SUCCESS;
-}
-/* }}} */
-
-int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa) /* {{{ */
-{
- zend_basic_block *blocks = ssa->cfg.blocks;
- zend_ssa_block *ssa_blocks;
- int blocks_count = ssa->cfg.blocks_count;
- uint32_t set_size;
- zend_bitset def, in, phi;
- int *var = NULL;
- int i, j, k, changed;
- zend_dfg dfg;
- ALLOCA_FLAG(dfg_use_heap)
- ALLOCA_FLAG(var_use_heap)
-
- if ((blocks_count * (op_array->last_var + op_array->T)) > 4 * 1024 * 1024) {
- /* Don't build SSA for very big functions */
- return FAILURE;
- }
-
- ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block));
- ssa->blocks = ssa_blocks;
-
- /* Compute Variable Liveness */
- dfg.vars = op_array->last_var + op_array->T;
- dfg.size = set_size = zend_bitset_len(dfg.vars);
- dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1), dfg_use_heap);
- memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1));
- dfg.def = dfg.tmp + set_size;
- dfg.use = dfg.def + set_size * blocks_count;
- dfg.in = dfg.use + set_size * blocks_count;
- dfg.out = dfg.in + set_size * blocks_count;
-
- if (zend_build_dfg(op_array, &ssa->cfg, &dfg, build_flags) != SUCCESS) {
- free_alloca(dfg.tmp, dfg_use_heap);
- return FAILURE;
- }
-
- if (build_flags & ZEND_SSA_DEBUG_LIVENESS) {
- zend_dump_dfg(op_array, &ssa->cfg, &dfg);
- }
-
- def = dfg.def;
- in = dfg.in;
-
- /* Reuse the "use" set, as we no longer need it */
- phi = dfg.use;
- zend_bitset_clear(phi, set_size * blocks_count);
-
- /* Place e-SSA pis. This will add additional "def" points, so it must
- * happen before def propagation. */
- place_essa_pis(arena, script, op_array, build_flags, ssa, &dfg);
-
- /* SSA construction, Step 1: Propagate "def" sets in merge points */
- do {
- changed = 0;
- for (j = 0; j < blocks_count; j++) {
- zend_bitset def_j = def + j * set_size, phi_j = phi + j * set_size;
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
- if (blocks[j].predecessors_count > 1) {
- if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) {
- /* Prevent any values from flowing into irreducible loops by
- replacing all incoming values with explicit phis. The
- register allocator depends on this property. */
- zend_bitset_union(phi_j, in + (j * set_size), set_size);
- } else {
- for (k = 0; k < blocks[j].predecessors_count; k++) {
- i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
- while (i != -1 && i != blocks[j].idom) {
- zend_bitset_union_with_intersection(
- phi_j, phi_j, def + (i * set_size), in + (j * set_size), set_size);
- i = blocks[i].idom;
- }
- }
- }
- if (!zend_bitset_subset(phi_j, def_j, set_size)) {
- zend_bitset_union(def_j, phi_j, set_size);
- changed = 1;
- }
- }
- }
- } while (changed);
-
- /* SSA construction, Step 2: Phi placement based on Dominance Frontiers */
- var = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), var_use_heap);
- if (!var) {
- free_alloca(dfg.tmp, dfg_use_heap);
- return FAILURE;
- }
-
- for (j = 0; j < blocks_count; j++) {
- if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
- if (!zend_bitset_empty(phi + j * set_size, set_size)) {
- ZEND_BITSET_REVERSE_FOREACH(phi + j * set_size, set_size, i) {
- zend_ssa_phi *phi = zend_arena_calloc(arena, 1,
- ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
- ZEND_MM_ALIGNED_SIZE(sizeof(int) * blocks[j].predecessors_count) +
- sizeof(void*) * blocks[j].predecessors_count);
-
- phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
- memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
- phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[j].predecessors_count));
-
- phi->pi = -1;
- phi->var = i;
- phi->ssa_var = -1;
-
- /* Place phis after pis */
- {
- zend_ssa_phi **pp = &ssa_blocks[j].phis;
- while (*pp) {
- if ((*pp)->pi < 0) {
- break;
- }
- pp = &(*pp)->next;
- }
- phi->next = *pp;
- *pp = phi;
- }
- } ZEND_BITSET_FOREACH_END();
- }
- }
-
- 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));
- memset(ssa->ops, 0xff, op_array->last * sizeof(zend_ssa_op));
- memset(var + op_array->last_var, 0xff, op_array->T * sizeof(int));
- /* Create uninitialized SSA variables for each CV */
- for (j = 0; j < op_array->last_var; j++) {
- var[j] = j;
- }
- ssa->vars_count = op_array->last_var;
- if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) != SUCCESS) {
- free_alloca(var, var_use_heap);
- free_alloca(dfg.tmp, dfg_use_heap);
- return FAILURE;
- }
-
- free_alloca(var, var_use_heap);
- free_alloca(dfg.tmp, dfg_use_heap);
-
- return SUCCESS;
-}
-/* }}} */
-
-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->result_use != op->op1_use && op->result_use != op->op2_use) {
- 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) {
- zend_ssa_phi *p;
-
- ZEND_ASSERT(phi->sources[0] >= 0);
- 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;
- }
- if (phi->has_range_constraint) {
- /* min and max variables can't be used together */
- zend_ssa_range_constraint *constraint = &phi->constraint.range;
- if (constraint->min_ssa_var >= 0) {
- phi->sym_use_chain = ssa_vars[constraint->min_ssa_var].sym_use_chain;
- ssa_vars[constraint->min_ssa_var].sym_use_chain = phi;
- } else if (constraint->max_ssa_var >= 0) {
- phi->sym_use_chain = ssa_vars[constraint->max_ssa_var].sym_use_chain;
- ssa_vars[constraint->max_ssa_var].sym_use_chain = phi;
- }
- }
- } else {
- int j;
-
- for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
- zend_ssa_phi *p;
-
- ZEND_ASSERT(phi->sources[j] >= 0);
- 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;
- }
- }
-
- /* Mark indirectly accessed variables */
- for (i = 0; i < op_array->last_var; i++) {
- if ((ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
- ssa_vars[i].alias = SYMTABLE_ALIAS;
- } else if (zend_string_equals_literal(op_array->vars[i], "http_response_header")) {
- ssa_vars[i].alias = HTTP_RESPONSE_HEADER_ALIAS;
- }
- }
- for (i = op_array->last_var; i < ssa->vars_count; i++) {
- if (ssa_vars[i].var < op_array->last_var) {
- ssa_vars[i].alias = ssa_vars[ssa_vars[i].var].alias;
- }
- }
-
- return SUCCESS;
-}
-/* }}} */
-
-int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */
-{
- if (ssa->vars[var].use_chain == op) {
- ssa->vars[var].use_chain = zend_ssa_next_use(ssa->ops, var, op);
- return 1;
- } else {
- int use = ssa->vars[var].use_chain;
-
- while (use >= 0) {
- if (ssa->ops[use].result_use == var) {
- if (ssa->ops[use].res_use_chain == op) {
- ssa->ops[use].res_use_chain = zend_ssa_next_use(ssa->ops, var, op);
- return 1;
- } else {
- use = ssa->ops[use].res_use_chain;
- }
- } else if (ssa->ops[use].op1_use == var) {
- if (ssa->ops[use].op1_use_chain == op) {
- ssa->ops[use].op1_use_chain = zend_ssa_next_use(ssa->ops, var, op);
- return 1;
- } else {
- use = ssa->ops[use].op1_use_chain;
- }
- } else if (ssa->ops[use].op2_use == var) {
- if (ssa->ops[use].op2_use_chain == op) {
- ssa->ops[use].op2_use_chain = zend_ssa_next_use(ssa->ops, var, op);
- return 1;
- } else {
- use = ssa->ops[use].op2_use_chain;
- }
- } else {
- break;
- }
- }
- /* something wrong */
- ZEND_UNREACHABLE();
- return 0;
- }
-}
-/* }}} */
-
-void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op) /* {{{ */
-{
- if (ssa_op->result_use >= 0) {
- zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->result_use);
- ssa_op->result_use = -1;
- ssa_op->res_use_chain = -1;
- }
- if (ssa_op->op1_use >= 0) {
- if (ssa_op->op1_use != ssa_op->op2_use) {
- zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->op1_use);
- } else {
- ssa_op->op2_use_chain = ssa_op->op1_use_chain;
- }
- ssa_op->op1_use = -1;
- ssa_op->op1_use_chain = -1;
- }
- if (ssa_op->op2_use >= 0) {
- zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->op2_use);
- ssa_op->op2_use = -1;
- ssa_op->op2_use_chain = -1;
- }
-
- /* We let the caller make sure that all defs are gone */
- ZEND_ASSERT(ssa_op->result_def == -1);
- ZEND_ASSERT(ssa_op->op1_def == -1);
- ZEND_ASSERT(ssa_op->op2_def == -1);
-
- MAKE_NOP(opline);
-}
-/* }}} */
-
-static inline zend_ssa_phi **zend_ssa_next_use_phi_ptr(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];
- }
- }
- }
- ZEND_UNREACHABLE();
- return NULL;
-}
-/* }}} */
-
-/* May be called even if source is not used in the phi (useful when removing uses in a phi
- * with multiple identical operands) */
-static inline void zend_ssa_remove_use_of_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int source, zend_ssa_phi *next_use_phi) /* {{{ */
-{
- zend_ssa_phi **cur = &ssa->vars[source].phi_use_chain;
- while (*cur && *cur != phi) {
- cur = zend_ssa_next_use_phi_ptr(ssa, source, *cur);
- }
- if (*cur) {
- *cur = next_use_phi;
- }
-}
-/* }}} */
-
-static void zend_ssa_remove_uses_of_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
-{
- int source;
- FOREACH_PHI_SOURCE(phi, source) {
- zend_ssa_remove_use_of_phi_source(ssa, phi, source, zend_ssa_next_use_phi(ssa, source, phi));
- } FOREACH_PHI_SOURCE_END();
-}
-/* }}} */
-
-static void zend_ssa_remove_phi_from_block(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
-{
- zend_ssa_block *block = &ssa->blocks[phi->block];
- zend_ssa_phi **cur = &block->phis;
- while (*cur != phi) {
- ZEND_ASSERT(*cur != NULL);
- cur = &(*cur)->next;
- }
- *cur = (*cur)->next;
-}
-/* }}} */
-
-static inline void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) /* {{{ */
-{
- if (ssa_op->op1_def >= 0) {
- zend_ssa_remove_uses_of_var(ssa, ssa_op->op1_def);
- zend_ssa_remove_op1_def(ssa, ssa_op);
- }
- if (ssa_op->op2_def >= 0) {
- zend_ssa_remove_uses_of_var(ssa, ssa_op->op2_def);
- zend_ssa_remove_op2_def(ssa, ssa_op);
- }
- if (ssa_op->result_def >= 0) {
- zend_ssa_remove_uses_of_var(ssa, ssa_op->result_def);
- zend_ssa_remove_result_def(ssa, ssa_op);
- }
-}
-/* }}} */
-
-static inline void zend_ssa_remove_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int pred_offset, int predecessors_count) /* {{{ */
-{
- int j, var_num = phi->sources[pred_offset];
- zend_ssa_phi *next_phi = phi->use_chains[pred_offset];
-
- predecessors_count--;
- if (pred_offset < predecessors_count) {
- memmove(phi->sources + pred_offset, phi->sources + pred_offset + 1, (predecessors_count - pred_offset) * sizeof(uint32_t));
- memmove(phi->use_chains + pred_offset, phi->use_chains + pred_offset + 1, (predecessors_count - pred_offset) * sizeof(zend_ssa_phi*));
- }
-
- /* Check if they same var is used in a different phi operand as well, in this case we don't
- * need to adjust the use chain (but may have to move the next pointer). */
- for (j = 0; j < predecessors_count; j++) {
- if (phi->sources[j] == var_num) {
- if (j < pred_offset) {
- ZEND_ASSERT(next_phi == NULL);
- } else if (j >= pred_offset) {
- phi->use_chains[j] = next_phi;
- }
- return;
- }
- }
-
- /* Variable only used in one operand, remove the phi from the use chain. */
- zend_ssa_remove_use_of_phi_source(ssa, phi, var_num, next_phi);
-}
-/* }}} */
-
-void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
-{
- ZEND_ASSERT(phi->ssa_var >= 0);
- ZEND_ASSERT(ssa->vars[phi->ssa_var].use_chain < 0
- && ssa->vars[phi->ssa_var].phi_use_chain == NULL);
- zend_ssa_remove_uses_of_phi_sources(ssa, phi);
- zend_ssa_remove_phi_from_block(ssa, phi);
- ssa->vars[phi->ssa_var].definition_phi = NULL;
- phi->ssa_var = -1;
-}
-/* }}} */
-
-void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num) /* {{{ */
-{
- zend_ssa_var *var = &ssa->vars[var_num];
- zend_ssa_phi *phi;
- int use;
- FOREACH_PHI_USE(var, phi) {
- int i, end = NUM_PHI_SOURCES(phi);
- for (i = 0; i < end; i++) {
- if (phi->sources[i] == var_num) {
- phi->use_chains[i] = NULL;
- }
- }
- } FOREACH_PHI_USE_END();
- var->phi_use_chain = NULL;
- FOREACH_USE(var, use) {
- zend_ssa_op *ssa_op = &ssa->ops[use];
- if (ssa_op->op1_use == var_num) {
- ssa_op->op1_use = -1;
- ssa_op->op1_use_chain = -1;
- }
- if (ssa_op->op2_use == var_num) {
- ssa_op->op2_use = -1;
- ssa_op->op2_use_chain = -1;
- }
- if (ssa_op->result_use == var_num) {
- ssa_op->result_use = -1;
- ssa_op->res_use_chain = -1;
- }
- } FOREACH_USE_END();
- var->use_chain = -1;
-}
-/* }}} */
-
-void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to) /* {{{ */
-{
- zend_basic_block *next_block = &ssa->cfg.blocks[to];
- zend_ssa_block *next_ssa_block = &ssa->blocks[to];
- zend_ssa_phi *phi;
- int j;
-
- /* Find at which predecessor offset this block is referenced */
- int pred_offset = -1;
- int *predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset];
-
- for (j = 0; j < next_block->predecessors_count; j++) {
- if (predecessors[j] == from) {
- pred_offset = j;
- break;
- }
- }
-
- /* If there are duplicate successors, the predecessors may have been removed in
- * a previous iteration already. */
- if (pred_offset == -1) {
- return;
- }
-
- /* For phis in successor blocks, remove the operands associated with this block */
- for (phi = next_ssa_block->phis; phi; phi = phi->next) {
- if (phi->pi >= 0) {
- if (phi->pi == from) {
- zend_ssa_rename_var_uses(ssa, phi->ssa_var, phi->sources[0], /* update_types */ 0);
- zend_ssa_remove_phi(ssa, phi);
- }
- } else {
- ZEND_ASSERT(phi->sources[pred_offset] >= 0);
- zend_ssa_remove_phi_source(ssa, phi, pred_offset, next_block->predecessors_count);
- }
- }
-
- /* Remove this predecessor */
- next_block->predecessors_count--;
- if (pred_offset < next_block->predecessors_count) {
- predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset + pred_offset];
- memmove(predecessors, predecessors + 1, (next_block->predecessors_count - pred_offset) * sizeof(uint32_t));
- }
-}
-/* }}} */
-
-void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{ */
-{
- zend_basic_block *block = &ssa->cfg.blocks[i];
- zend_ssa_block *ssa_block = &ssa->blocks[i];
- int *predecessors;
- zend_ssa_phi *phi;
- int j, s;
-
- block->flags &= ~ZEND_BB_REACHABLE;
-
- /* Removes phis in this block */
- for (phi = ssa_block->phis; phi; phi = phi->next) {
- zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
- zend_ssa_remove_phi(ssa, phi);
- }
-
- /* Remove instructions in this block */
- for (j = block->start; j < block->start + block->len; j++) {
- if (op_array->opcodes[j].opcode == ZEND_NOP) {
- continue;
- }
-
- zend_ssa_remove_defs_of_instr(ssa, &ssa->ops[j]);
- zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]);
- }
-
- for (s = 0; s < block->successors_count; s++) {
- zend_ssa_remove_predecessor(ssa, i, block->successors[s]);
- }
-
- /* Remove successors of predecessors */
- predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
- for (j = 0; j < block->predecessors_count; j++) {
- if (predecessors[j] >= 0) {
- zend_basic_block *prev_block = &ssa->cfg.blocks[predecessors[j]];
-
- for (s = 0; s < prev_block->successors_count; s++) {
- if (prev_block->successors[s] == i) {
- memmove(prev_block->successors + s,
- prev_block->successors + s + 1,
- sizeof(int) * (prev_block->successors_count - s - 1));
- prev_block->successors_count--;
- s--;
- }
- }
- }
- }
-
- block->successors_count = 0;
- block->predecessors_count = 0;
-
- /* Remove from dominators tree */
- if (block->idom >= 0) {
- j = ssa->cfg.blocks[block->idom].children;
- if (j == i) {
- ssa->cfg.blocks[block->idom].children = block->next_child;
- } else if (j >= 0) {
- while (ssa->cfg.blocks[j].next_child >= 0) {
- if (ssa->cfg.blocks[j].next_child == i) {
- ssa->cfg.blocks[j].next_child = block->next_child;
- break;
- }
- j = ssa->cfg.blocks[j].next_child;
- }
- }
- }
- block->idom = -1;
- block->level = -1;
- block->children = -1;
- block->next_child = -1;
-}
-/* }}} */
-
-static void propagate_phi_type_widening(zend_ssa *ssa, int var) /* {{{ */
-{
- zend_ssa_phi *phi;
- FOREACH_PHI_USE(&ssa->vars[var], phi) {
- if (ssa->var_info[var].type & ~ssa->var_info[phi->ssa_var].type) {
- ssa->var_info[phi->ssa_var].type |= ssa->var_info[var].type;
- propagate_phi_type_widening(ssa, phi->ssa_var);
- }
- } FOREACH_PHI_USE_END();
-}
-/* }}} */
-
-void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, zend_bool update_types) /* {{{ */
-{
- zend_ssa_var *old_var = &ssa->vars[old];
- zend_ssa_var *new_var = &ssa->vars[new];
- int use;
- zend_ssa_phi *phi;
-
- ZEND_ASSERT(old >= 0 && new >= 0);
- ZEND_ASSERT(old != new);
-
- /* Only a no_val is both variables are */
- new_var->no_val &= old_var->no_val;
-
- /* Update ssa_op use chains */
- FOREACH_USE(old_var, use) {
- zend_ssa_op *ssa_op = &ssa->ops[use];
-
- /* If the op already uses the new var, don't add the op to the use
- * list again. Instead move the use_chain to the correct operand. */
- zend_bool add_to_use_chain = 1;
- if (ssa_op->result_use == new) {
- add_to_use_chain = 0;
- } else if (ssa_op->op1_use == new) {
- if (ssa_op->result_use == old) {
- ssa_op->res_use_chain = ssa_op->op1_use_chain;
- ssa_op->op1_use_chain = -1;
- }
- add_to_use_chain = 0;
- } else if (ssa_op->op2_use == new) {
- if (ssa_op->result_use == old) {
- ssa_op->res_use_chain = ssa_op->op2_use_chain;
- ssa_op->op2_use_chain = -1;
- } else if (ssa_op->op1_use == old) {
- ssa_op->op1_use_chain = ssa_op->op2_use_chain;
- ssa_op->op2_use_chain = -1;
- }
- add_to_use_chain = 0;
- }
-
- /* Perform the actual renaming */
- if (ssa_op->result_use == old) {
- ssa_op->result_use = new;
- }
- if (ssa_op->op1_use == old) {
- ssa_op->op1_use = new;
- }
- if (ssa_op->op2_use == old) {
- ssa_op->op2_use = new;
- }
-
- /* Add op to use chain of new var (if it isn't already). We use the
- * first use chain of (result, op1, op2) that has the new variable. */
- if (add_to_use_chain) {
- if (ssa_op->result_use == new) {
- ssa_op->res_use_chain = new_var->use_chain;
- new_var->use_chain = use;
- } else if (ssa_op->op1_use == new) {
- ssa_op->op1_use_chain = new_var->use_chain;
- new_var->use_chain = use;
- } else {
- ZEND_ASSERT(ssa_op->op2_use == new);
- ssa_op->op2_use_chain = new_var->use_chain;
- new_var->use_chain = use;
- }
- }
- } FOREACH_USE_END();
- old_var->use_chain = -1;
-
- /* Update phi use chains */
- FOREACH_PHI_USE(old_var, phi) {
- int j;
- zend_bool after_first_new_source = 0;
-
- /* If the phi already uses the new var, find its use chain, as we may
- * need to move it to a different source operand. */
- zend_ssa_phi **existing_use_chain_ptr = NULL;
- for (j = 0; j < ssa->cfg.blocks[phi->block].predecessors_count; j++) {
- if (phi->sources[j] == new) {
- existing_use_chain_ptr = &phi->use_chains[j];
- break;
- }
- }
-
- for (j = 0; j < ssa->cfg.blocks[phi->block].predecessors_count; j++) {
- if (phi->sources[j] == new) {
- after_first_new_source = 1;
- } else if (phi->sources[j] == old) {
- phi->sources[j] = new;
-
- /* Either move existing use chain to this source, or add the phi
- * to the phi use chain of the new variables. Do this only once. */
- if (!after_first_new_source) {
- if (existing_use_chain_ptr) {
- phi->use_chains[j] = *existing_use_chain_ptr;
- *existing_use_chain_ptr = NULL;
- } else {
- phi->use_chains[j] = new_var->phi_use_chain;
- new_var->phi_use_chain = phi;
- }
- after_first_new_source = 1;
- } else {
- phi->use_chains[j] = NULL;
- }
- }
- }
-
- /* Make sure phi result types are not incorrectly narrow after renaming.
- * This should not normally happen, but can occur if we DCE an assignment
- * or unset and there is an improper phi-indirected use lateron. */
- // TODO Alternatively we could rerun type-inference after DCE
- if (update_types && (ssa->var_info[new].type & ~ssa->var_info[phi->ssa_var].type)) {
- ssa->var_info[phi->ssa_var].type |= ssa->var_info[new].type;
- propagate_phi_type_widening(ssa, phi->ssa_var);
- }
- } FOREACH_PHI_USE_END();
- old_var->phi_use_chain = NULL;
-}
-/* }}} */
diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h
deleted file mode 100644
index a5d6362f47..0000000000
--- a/ext/opcache/Optimizer/zend_ssa.h
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine, SSA - Static Single Assignment Form |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef ZEND_SSA_H
-#define ZEND_SSA_H
-
-#include "zend_optimizer.h"
-#include "zend_cfg.h"
-
-typedef struct _zend_ssa_range {
- zend_long min;
- zend_long max;
- zend_bool underflow;
- zend_bool overflow;
-} zend_ssa_range;
-
-typedef enum _zend_ssa_negative_lat {
- NEG_NONE = 0,
- NEG_INIT = 1,
- NEG_INVARIANT = 2,
- NEG_USE_LT = 3,
- NEG_USE_GT = 4,
- NEG_UNKNOWN = 5
-} zend_ssa_negative_lat;
-
-/* Special kind of SSA Phi function used in eSSA */
-typedef struct _zend_ssa_range_constraint {
- zend_ssa_range range; /* simple range constraint */
- int min_var;
- int max_var;
- int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */
- int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */
- zend_ssa_negative_lat negative;
-} zend_ssa_range_constraint;
-
-typedef struct _zend_ssa_type_constraint {
- uint32_t type_mask; /* Type mask to intersect with */
- zend_class_entry *ce; /* Class entry for instanceof constraints */
-} zend_ssa_type_constraint;
-
-typedef union _zend_ssa_pi_constraint {
- zend_ssa_range_constraint range;
- zend_ssa_type_constraint type;
-} zend_ssa_pi_constraint;
-
-/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */
-typedef struct _zend_ssa_phi zend_ssa_phi;
-struct _zend_ssa_phi {
- zend_ssa_phi *next; /* next Phi in the same BB */
- int pi; /* if >= 0 this is actually a e-SSA Pi */
- zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */
- int var; /* Original CV, VAR or TMP variable index */
- int ssa_var; /* SSA variable index */
- int block; /* current BB index */
- int visited : 1; /* flag to avoid recursive processing */
- int has_range_constraint : 1;
- zend_ssa_phi **use_chains;
- zend_ssa_phi *sym_use_chain;
- int *sources; /* Array of SSA IDs that produce this var.
- As many as this block has
- predecessors. */
-};
-
-typedef struct _zend_ssa_block {
- zend_ssa_phi *phis;
-} zend_ssa_block;
-
-typedef struct _zend_ssa_op {
- int op1_use;
- int op2_use;
- int result_use;
- int op1_def;
- int op2_def;
- int result_def;
- int op1_use_chain;
- int op2_use_chain;
- int res_use_chain;
-} zend_ssa_op;
-
-typedef enum _zend_ssa_alias_kind {
- NO_ALIAS,
- SYMTABLE_ALIAS,
- HTTP_RESPONSE_HEADER_ALIAS
-} zend_ssa_alias_kind;
-
-typedef enum _zend_ssa_escape_state {
- ESCAPE_STATE_UNKNOWN,
- ESCAPE_STATE_NO_ESCAPE,
- ESCAPE_STATE_FUNCTION_ESCAPE,
- ESCAPE_STATE_GLOBAL_ESCAPE
-} zend_ssa_escape_state;
-
-typedef struct _zend_ssa_var {
- int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */
- int scc; /* strongly connected component */
- int definition; /* opcode that defines this value */
- zend_ssa_phi *definition_phi; /* phi that defines this value */
- int use_chain; /* uses of this value, linked through opN_use_chain */
- zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */
- zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constraints */
- unsigned int no_val : 1; /* value doesn't matter (used as op1 in ZEND_ASSIGN) */
- unsigned int scc_entry : 1;
- unsigned int alias : 2; /* value may be changed indirectly */
- unsigned int escape_state : 2;
-} 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;
- unsigned int delayed_fetch_this : 1;
- unsigned int avoid_refcounting : 1;
- unsigned int guarded_reference : 1;
- unsigned int indirect_reference : 1; /* IS_INDIRECT returned by FETCH_DIM_W/FETCH_OBJ_W */
-} zend_ssa_var_info;
-
-typedef struct _zend_ssa {
- zend_cfg cfg; /* control flow graph */
- int vars_count; /* number of SSA variables */
- int sccs; /* number of SCCs */
- 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 */
- zend_ssa_var_info *var_info;
-} zend_ssa;
-
-BEGIN_EXTERN_C()
-
-int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa);
-int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var);
-int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
-int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
-
-void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to);
-void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
-void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
-void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
-void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b);
-void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, zend_bool update_types);
-
-static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var)
-{
- ZEND_ASSERT(var->definition >= 0);
- ZEND_ASSERT(var->use_chain < 0);
- ZEND_ASSERT(!var->phi_use_chain);
- var->definition = -1;
-}
-
-static zend_always_inline void zend_ssa_remove_result_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
-{
- zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
- _zend_ssa_remove_def(var);
- ssa_op->result_def = -1;
-}
-
-static zend_always_inline void zend_ssa_remove_op1_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
-{
- zend_ssa_var *var = &ssa->vars[ssa_op->op1_def];
- _zend_ssa_remove_def(var);
- ssa_op->op1_def = -1;
-}
-
-static zend_always_inline void zend_ssa_remove_op2_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
-{
- zend_ssa_var *var = &ssa->vars[ssa_op->op2_def];
- _zend_ssa_remove_def(var);
- ssa_op->op2_def = -1;
-}
-
-END_EXTERN_C()
-
-static zend_always_inline int zend_ssa_next_use(const zend_ssa_op *ssa_op, int var, int use)
-{
- ssa_op += use;
- if (ssa_op->op1_use == var) {
- return ssa_op->op1_use_chain;
- } else if (ssa_op->op2_use == var) {
- return ssa_op->op2_use_chain;
- } else {
- return ssa_op->res_use_chain;
- }
-}
-
-static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ssa, int var, const 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;
-}
-
-static zend_always_inline zend_bool zend_ssa_is_no_val_use(const zend_op *opline, const zend_ssa_op *ssa_op, int var)
-{
- if (opline->opcode == ZEND_ASSIGN
- || opline->opcode == ZEND_UNSET_CV
- || opline->opcode == ZEND_BIND_GLOBAL
- || opline->opcode == ZEND_BIND_STATIC) {
- return ssa_op->op1_use == var && ssa_op->op2_use != var;
- }
- if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
- return ssa_op->op2_use == var && ssa_op->op1_use != var;
- }
- if (ssa_op->result_use == var
- && opline->opcode != ZEND_ADD_ARRAY_ELEMENT
- && opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
- return ssa_op->op1_use != var && ssa_op->op2_use != var;
- }
- return 0;
-}
-
-static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) {
- /* Rename def to use if possible. Mark variable as not defined otherwise. */
- if (ssa_op->op1_def >= 0) {
- if (ssa_op->op1_use >= 0) {
- zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
- }
- ssa->vars[ssa_op->op1_def].definition = -1;
- ssa_op->op1_def = -1;
- }
- if (ssa_op->op2_def >= 0) {
- if (ssa_op->op2_use >= 0) {
- zend_ssa_rename_var_uses(ssa, ssa_op->op2_def, ssa_op->op2_use, 1);
- }
- ssa->vars[ssa_op->op2_def].definition = -1;
- ssa_op->op2_def = -1;
- }
- if (ssa_op->result_def >= 0) {
- if (ssa_op->result_use >= 0) {
- zend_ssa_rename_var_uses(ssa, ssa_op->result_def, ssa_op->result_use, 1);
- }
- ssa->vars[ssa_op->result_def].definition = -1;
- ssa_op->result_def = -1;
- }
-}
-
-#define NUM_PHI_SOURCES(phi) \
- ((phi)->pi >= 0 ? 1 : (ssa->cfg.blocks[(phi)->block].predecessors_count))
-
-/* FOREACH_USE and FOREACH_PHI_USE explicitly support "continue"
- * and changing the use chain of the current element */
-#define FOREACH_USE(var, use) do { \
- int _var_num = (var) - ssa->vars, next; \
- for (use = (var)->use_chain; use >= 0; use = next) { \
- next = zend_ssa_next_use(ssa->ops, _var_num, use);
-#define FOREACH_USE_END() \
- } \
-} while (0)
-
-#define FOREACH_PHI_USE(var, phi) do { \
- int _var_num = (var) - ssa->vars; \
- zend_ssa_phi *next_phi; \
- for (phi = (var)->phi_use_chain; phi; phi = next_phi) { \
- next_phi = zend_ssa_next_use_phi(ssa, _var_num, phi);
-#define FOREACH_PHI_USE_END() \
- } \
-} while (0)
-
-#define FOREACH_PHI_SOURCE(phi, source) do { \
- zend_ssa_phi *_phi = (phi); \
- int _i, _end = NUM_PHI_SOURCES(phi); \
- for (_i = 0; _i < _end; _i++) { \
- ZEND_ASSERT(_phi->sources[_i] >= 0); \
- source = _phi->sources[_i];
-#define FOREACH_PHI_SOURCE_END() \
- } \
-} while (0)
-
-#define FOREACH_PHI(phi) do { \
- int _i; \
- for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
- phi = ssa->blocks[_i].phis; \
- for (; phi; phi = phi->next) {
-#define FOREACH_PHI_END() \
- } \
- } \
-} while (0)
-
-#define FOREACH_BLOCK(block) do { \
- int _i; \
- for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
- (block) = &ssa->cfg.blocks[_i]; \
- if (!((block)->flags & ZEND_BB_REACHABLE)) { \
- continue; \
- }
-#define FOREACH_BLOCK_END() \
- } \
-} while (0)
-
-/* Does not support "break" */
-#define FOREACH_INSTR_NUM(i) do { \
- zend_basic_block *_block; \
- FOREACH_BLOCK(_block) { \
- uint32_t _end = _block->start + _block->len; \
- for ((i) = _block->start; (i) < _end; (i)++) {
-#define FOREACH_INSTR_NUM_END() \
- } \
- } FOREACH_BLOCK_END(); \
-} while (0)
-
-#endif /* ZEND_SSA_H */
diff --git a/ext/opcache/Optimizer/zend_worklist.h b/ext/opcache/Optimizer/zend_worklist.h
deleted file mode 100644
index 2f3e3dd979..0000000000
--- a/ext/opcache/Optimizer/zend_worklist.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- +----------------------------------------------------------------------+
- | Zend Engine |
- +----------------------------------------------------------------------+
- | Copyright (c) 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: Andy Wingo <wingo@igalia.com> |
- +----------------------------------------------------------------------+
-*/
-
-#ifndef _ZEND_WORKLIST_H_
-#define _ZEND_WORKLIST_H_
-
-#include "zend_arena.h"
-#include "zend_bitset.h"
-
-typedef struct _zend_worklist_stack {
- int *buf;
- int len;
- int capacity;
-} zend_worklist_stack;
-
-#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
- (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
- (s)->len = 0; \
- (s)->capacity = _len; \
- } while (0)
-
-#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
- free_alloca((s)->buf, use_heap)
-
-static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
-{
- ZEND_ASSERT(len >= 0);
-
- stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
- stack->len = 0;
- stack->capacity = len;
-
- return SUCCESS;
-}
-
-static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
-{
- ZEND_ASSERT(stack->len < stack->capacity);
- stack->buf[stack->len++] = i;
-}
-
-static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
-{
- ZEND_ASSERT(stack->len);
- return stack->buf[stack->len - 1];
-}
-
-static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
-{
- ZEND_ASSERT(stack->len);
- return stack->buf[--stack->len];
-}
-
-typedef struct _zend_worklist {
- zend_bitset visited;
- zend_worklist_stack stack;
-} zend_worklist;
-
-#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
- (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
- (w)->stack.len = 0; \
- (w)->stack.capacity = _len; \
- (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
- memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
- } while (0)
-
-#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
- free_alloca((w)->stack.buf, use_heap)
-
-static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
-{
- ZEND_ASSERT(len >= 0);
- worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
- return zend_worklist_stack_prepare(arena, &worklist->stack, len);
-}
-
-static inline int zend_worklist_len(zend_worklist *worklist)
-{
- return worklist->stack.len;
-}
-
-static inline int zend_worklist_push(zend_worklist *worklist, int i)
-{
- ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
-
- if (zend_bitset_in(worklist->visited, i)) {
- return 0;
- }
-
- zend_bitset_incl(worklist->visited, i);
- zend_worklist_stack_push(&worklist->stack, i);
- return 1;
-}
-
-static inline int zend_worklist_peek(zend_worklist *worklist)
-{
- return zend_worklist_stack_peek(&worklist->stack);
-}
-
-static inline int zend_worklist_pop(zend_worklist *worklist)
-{
- /* Does not clear visited flag */
- return zend_worklist_stack_pop(&worklist->stack);
-}
-
-#endif /* _ZEND_WORKLIST_H_ */
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 6974307e29..088bca6ebf 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -109,12 +109,12 @@ zend_accel_shared_globals *accel_shared_globals = NULL;
#ifdef ZEND_WIN32
char accel_uname_id[32];
#endif
-zend_bool accel_startup_ok = 0;
+bool accel_startup_ok = 0;
static char *zps_failure_reason = NULL;
char *zps_api_failure_reason = NULL;
-zend_bool file_cache_only = 0; /* process uses file cache only */
+bool file_cache_only = 0; /* process uses file cache only */
#if ENABLE_FILE_CACHE_FALLBACK
-zend_bool fallback_process = 0; /* process uses file cache fallback */
+bool fallback_process = 0; /* process uses file cache fallback */
#endif
static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type);
@@ -805,7 +805,7 @@ static inline void kill_all_lockers(struct flock *mem_usage_check)
/* errno is not ESRCH or we ran out of tries to kill the locker */
ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */
/* cannot kill the locker, bail out with error */
- zend_accel_error(ACCEL_LOG_ERROR, "Cannot kill process %d!", mem_usage_check->l_pid);
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot kill process %d!", mem_usage_check->l_pid);
}
mem_usage_check->l_type = F_WRLCK;
@@ -1281,7 +1281,7 @@ char *accel_make_persistent_key(const char *path, size_t path_length, int *key_l
return (char*)path;
}
-int zend_accel_invalidate(const char *filename, size_t filename_len, zend_bool force)
+int zend_accel_invalidate(const char *filename, size_t filename_len, bool force)
{
zend_string *realpath;
zend_persistent_script *persistent_script;
@@ -1356,7 +1356,7 @@ static void zend_accel_add_key(const char *key, unsigned int key_length, zend_ac
}
}
-static zend_always_inline zend_bool is_phar_file(zend_string *filename)
+static zend_always_inline bool is_phar_file(zend_string *filename)
{
return filename && ZSTR_LEN(filename) >= sizeof(".phar") &&
!memcmp(ZSTR_VAL(filename) + ZSTR_LEN(filename) - (sizeof(".phar")-1), ".phar", sizeof(".phar")-1) &&
@@ -2642,7 +2642,7 @@ static int zend_accel_init_shm(void)
accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals) + sizeof(uint32_t));
}
if (!accel_shared_globals) {
- zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
zend_shared_alloc_unlock();
return FAILURE;
}
@@ -2960,9 +2960,6 @@ static zend_result accel_post_startup(void)
}
}
- /* Initialize zend_func_info_rid */
- zend_optimizer_startup();
-
/********************************************/
/* End of non-SHM dependent initializations */
/********************************************/
@@ -2971,7 +2968,7 @@ static zend_result accel_post_startup(void)
size_t shm_size = ZCG(accel_directives).memory_consumption;
#ifdef HAVE_JIT
size_t jit_size = 0;
- zend_bool reattached = 0;
+ bool reattached = 0;
if (JIT_G(enabled) && JIT_G(buffer_size)
&& zend_jit_check_support() == SUCCESS) {
@@ -2985,7 +2982,7 @@ static zend_result accel_post_startup(void)
page_size = getpagesize();
# endif
if (!page_size || (page_size & (page_size - 1))) {
- zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can't get page size.");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can't get page size.");
abort();
}
jit_size = JIT_G(buffer_size);
@@ -3005,7 +3002,7 @@ static zend_result accel_post_startup(void)
break;
case ALLOC_FAILURE:
accel_startup_ok = 0;
- zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
return SUCCESS;
case SUCCESSFULLY_REATTACHED:
#if defined(HAVE_JIT) && !defined(ZEND_WIN32)
@@ -3018,7 +3015,7 @@ static zend_result accel_post_startup(void)
break;
case FAILED_REATTACHED:
accel_startup_ok = 0;
- zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
return SUCCESS;
break;
#if ENABLE_FILE_CACHE_FALLBACK
@@ -3058,7 +3055,7 @@ static zend_result accel_post_startup(void)
SHM_PROTECT();
} else if (!ZCG(accel_directives).file_cache) {
accel_startup_ok = 0;
- zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
return SUCCESS;
} else {
#ifdef HAVE_JIT
@@ -3122,8 +3119,6 @@ file_cache_fallback:
zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
}
- zend_optimizer_startup();
-
if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) {
accel_use_shm_interned_strings();
}
@@ -3141,14 +3136,12 @@ static void accel_post_shutdown(void)
void accel_shutdown(void)
{
zend_ini_entry *ini_entry;
- zend_bool _file_cache_only = 0;
+ bool _file_cache_only = 0;
#ifdef HAVE_JIT
zend_jit_shutdown();
#endif
- zend_optimizer_shutdown();
-
zend_accel_blacklist_shutdown(&accel_blacklist);
if (!ZCG(enabled) || !accel_startup_ok) {
@@ -3507,7 +3500,7 @@ static void get_unresolved_initializer(zend_class_entry *ce, const char **kind,
} ZEND_HASH_FOREACH_END();
}
-static zend_bool preload_needed_types_known(zend_class_entry *ce);
+static bool preload_needed_types_known(zend_class_entry *ce);
static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) {
zend_class_entry *p;
*kind = "Unknown reason";
@@ -3564,9 +3557,9 @@ static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, con
}
}
-static zend_bool preload_try_resolve_constants(zend_class_entry *ce)
+static bool preload_try_resolve_constants(zend_class_entry *ce)
{
- zend_bool ok, changed;
+ bool ok, changed;
zend_class_constant *c;
zval *val;
@@ -3638,9 +3631,9 @@ static zend_class_entry *preload_fetch_resolved_ce(zend_string *name, zend_class
return ce;
}
-static zend_bool preload_try_resolve_property_types(zend_class_entry *ce)
+static bool preload_try_resolve_property_types(zend_class_entry *ce)
{
- zend_bool ok = 1;
+ bool ok = 1;
if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
zend_property_info *prop;
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
@@ -3662,7 +3655,7 @@ static zend_bool preload_try_resolve_property_types(zend_class_entry *ce)
return ok;
}
-static zend_bool preload_is_class_type_known(zend_class_entry *ce, zend_string *name) {
+static bool preload_is_class_type_known(zend_class_entry *ce, zend_string *name) {
if (zend_string_equals_literal_ci(name, "self") ||
zend_string_equals_literal_ci(name, "parent") ||
zend_string_equals_ci(name, ce->name)) {
@@ -3670,12 +3663,12 @@ static zend_bool preload_is_class_type_known(zend_class_entry *ce, zend_string *
}
zend_string *lcname = zend_string_tolower(name);
- zend_bool known = zend_hash_exists(EG(class_table), lcname);
+ bool known = zend_hash_exists(EG(class_table), lcname);
zend_string_release(lcname);
return known;
}
-static zend_bool preload_is_type_known(zend_class_entry *ce, zend_type *type) {
+static bool preload_is_type_known(zend_class_entry *ce, zend_type *type) {
zend_type *single_type;
ZEND_TYPE_FOREACH(*type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)) {
@@ -3687,7 +3680,7 @@ static zend_bool preload_is_type_known(zend_class_entry *ce, zend_type *type) {
return 1;
}
-static zend_bool preload_is_method_maybe_override(zend_class_entry *ce, zend_string *lcname) {
+static bool preload_is_method_maybe_override(zend_class_entry *ce, zend_string *lcname) {
zend_class_entry *p;
if (ce->trait_aliases || ce->trait_precedences) {
return 1;
@@ -3725,7 +3718,7 @@ static zend_bool preload_is_method_maybe_override(zend_class_entry *ce, zend_str
return 0;
}
-static zend_bool preload_needed_types_known(zend_class_entry *ce) {
+static bool preload_needed_types_known(zend_class_entry *ce) {
zend_function *fptr;
zend_string *lcname;
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, lcname, fptr) {
@@ -3752,7 +3745,7 @@ static void preload_link(void)
zend_persistent_script *script;
zend_class_entry *ce, *parent, *p;
zend_string *key;
- zend_bool found, changed;
+ bool found, changed;
uint32_t i;
dtor_func_t orig_dtor;
zend_function *function;
@@ -4055,7 +4048,7 @@ static zend_string *preload_resolve_path(zend_string *filename)
static void preload_remove_empty_includes(void)
{
zend_persistent_script *script;
- zend_bool changed;
+ bool changed;
/* mark all as empty */
ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
@@ -4224,7 +4217,7 @@ static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_s
uint32_t checkpoint;
if (zend_accel_hash_is_full(&ZCSG(hash))) {
- zend_accel_error(ACCEL_LOG_FATAL, "Not enough entries in hash table for preloading. Consider increasing the value for the opcache.max_accelerated_files directive in php.ini.");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Not enough entries in hash table for preloading. Consider increasing the value for the opcache.max_accelerated_files directive in php.ini.");
return NULL;
}
@@ -4276,7 +4269,7 @@ static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_s
}
#endif
if (!ZCG(mem)) {
- zend_accel_error(ACCEL_LOG_FATAL, "Not enough shared memory for preloading. Consider increasing the value for the opcache.memory_consumption directive in php.ini.");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Not enough shared memory for preloading. Consider increasing the value for the opcache.memory_consumption directive in php.ini.");
return NULL;
}
@@ -4362,7 +4355,7 @@ static zend_result preload_autoload(zend_string *filename)
zend_op_array *op_array;
zend_execute_data *old_execute_data;
zend_class_entry *old_fake_scope;
- zend_bool do_bailout = 0;
+ bool do_bailout = 0;
int ret = SUCCESS;
if (zend_hash_exists(&EG(included_files), filename)) {
@@ -4425,7 +4418,7 @@ static zend_result preload_autoload(zend_string *filename)
return ret;
}
-static int accel_preload(const char *config, zend_bool in_child)
+static int accel_preload(const char *config, bool in_child)
{
zend_file_handle file_handle;
int ret;
@@ -4598,7 +4591,7 @@ static int accel_preload(const char *config, zend_bool in_child)
CG(map_ptr_last) = orig_map_ptr_last;
if (EG(full_tables_cleanup)) {
- zend_accel_error(ACCEL_LOG_FATAL, "Preloading is not compatible with dl() function.");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading is not compatible with dl() function.");
ret = FAILURE;
goto finish;
}
@@ -4681,7 +4674,7 @@ static int accel_preload(const char *config, zend_bool in_child)
zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
if (preload_optimize(script) != SUCCESS) {
- zend_accel_error(ACCEL_LOG_FATAL, "Optimization error during preloading!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Optimization error during preloading!");
return FAILURE;
}
@@ -4780,7 +4773,7 @@ static int accel_finish_startup(void)
if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) {
#ifdef ZEND_WIN32
- zend_accel_error(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
return FAILURE;
#else
int in_child = 0;
@@ -4798,7 +4791,7 @@ static int accel_finish_startup(void)
size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
void (*orig_flush)(void *server_context) = sapi_module.flush;
#ifdef ZEND_SIGNALS
- zend_bool old_reset_signals = SIGG(reset);
+ bool old_reset_signals = SIGG(reset);
#endif
if (UNEXPECTED(file_cache_only)) {
@@ -4823,21 +4816,21 @@ static int accel_finish_startup(void)
if (!ZCG(accel_directives).preload_user
|| !*ZCG(accel_directives).preload_user) {
zend_shared_alloc_unlock();
- zend_accel_error(ACCEL_LOG_FATAL, "\"opcache.preload_user\" has not been defined");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload_user\" has not been defined");
return FAILURE;
}
pw = getpwnam(ZCG(accel_directives).preload_user);
if (pw == NULL) {
zend_shared_alloc_unlock();
- zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
return FAILURE;
}
pid = fork();
if (pid == -1) {
zend_shared_alloc_unlock();
- zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to fork()");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()");
return FAILURE;
} else if (pid == 0) { /* children */
if (setgid(pw->pw_gid) < 0) {
@@ -4858,7 +4851,7 @@ static int accel_finish_startup(void)
if (waitpid(pid, &status, 0) < 0) {
zend_shared_alloc_unlock();
- zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
return FAILURE;
}
@@ -4904,7 +4897,7 @@ static int accel_finish_startup(void)
EG(error_reporting) = orig_error_reporting;
if (rc == SUCCESS) {
- zend_bool orig_report_memleaks;
+ bool orig_report_memleaks;
/* don't send headers */
SG(headers_sent) = 1;
diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h
index b9e3f2a81d..1d151b982e 100644
--- a/ext/opcache/ZendAccelerator.h
+++ b/ext/opcache/ZendAccelerator.h
@@ -121,9 +121,9 @@ typedef struct _zend_persistent_script {
zend_long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */
int ping_auto_globals_mask; /* which autoglobals are used by the script */
accel_time_t timestamp; /* the script modification time */
- zend_bool corrupted;
- zend_bool is_phar;
- zend_bool empty;
+ bool corrupted;
+ bool is_phar;
+ bool empty;
uint32_t num_warnings;
zend_recorded_warning **warnings;
@@ -155,18 +155,18 @@ typedef struct _zend_accel_directives {
char *user_blacklist_filename;
zend_long consistency_checks;
zend_long force_restart_timeout;
- zend_bool use_cwd;
- zend_bool ignore_dups;
- zend_bool validate_timestamps;
- zend_bool revalidate_path;
- zend_bool save_comments;
- zend_bool record_warnings;
- zend_bool protect_memory;
- zend_bool file_override_enabled;
- zend_bool enable_cli;
- zend_bool validate_permission;
+ bool use_cwd;
+ bool ignore_dups;
+ bool validate_timestamps;
+ bool revalidate_path;
+ bool save_comments;
+ bool record_warnings;
+ bool protect_memory;
+ bool file_override_enabled;
+ bool enable_cli;
+ bool validate_permission;
#ifndef ZEND_WIN32
- zend_bool validate_root;
+ bool validate_root;
#endif
zend_ulong revalidate_freq;
zend_ulong file_update_protection;
@@ -186,13 +186,13 @@ typedef struct _zend_accel_directives {
char *lockfile_path;
#endif
char *file_cache;
- zend_bool file_cache_only;
- zend_bool file_cache_consistency_checks;
+ bool file_cache_only;
+ bool file_cache_consistency_checks;
#if ENABLE_FILE_CACHE_FALLBACK
- zend_bool file_cache_fallback;
+ bool file_cache_fallback;
#endif
#ifdef HAVE_HUGE_CODE_PAGES
- zend_bool huge_code_pages;
+ bool huge_code_pages;
#endif
char *preload;
#ifndef ZEND_WIN32
@@ -205,11 +205,10 @@ typedef struct _zend_accel_directives {
typedef struct _zend_accel_globals {
int counted; /* the process uses shared memory */
- zend_bool enabled;
- zend_bool locked; /* thread obtained exclusive lock */
- zend_bool accelerator_enabled; /* accelerator enabled for current request */
- zend_bool pcre_reseted;
- HashTable bind_hash; /* prototype and zval lookup table */
+ bool enabled;
+ bool locked; /* thread obtained exclusive lock */
+ bool accelerator_enabled; /* accelerator enabled for current request */
+ bool pcre_reseted;
zend_accel_directives accel_directives;
zend_string *cwd; /* current working directory or NULL */
zend_string *include_path; /* current value of "include_path" directive */
@@ -230,9 +229,9 @@ typedef struct _zend_accel_globals {
void *mem;
void *arena_mem;
zend_persistent_script *current_persistent_script;
- zend_bool is_immutable_class;
+ bool is_immutable_class;
/* Temporary storage for warnings before they are moved into persistent_script. */
- zend_bool record_warnings;
+ bool record_warnings;
uint32_t num_warnings;
zend_recorded_warning **warnings;
/* cache to save hash lookup on the same INCLUDE opcode */
@@ -268,15 +267,15 @@ typedef struct _zend_accel_shared_globals {
time_t start_time;
time_t last_restart_time;
time_t force_restart_time;
- zend_bool accelerator_enabled;
- zend_bool restart_pending;
+ bool accelerator_enabled;
+ bool restart_pending;
zend_accel_restart_reason restart_reason;
- zend_bool cache_status_before_restart;
+ bool cache_status_before_restart;
#ifdef ZEND_WIN32
LONGLONG mem_usage;
LONGLONG restart_in;
#endif
- zend_bool restart_in_progress;
+ bool restart_in_progress;
/* Preloading */
zend_persistent_script *preload_script;
@@ -292,10 +291,10 @@ typedef struct _zend_accel_shared_globals {
#ifdef ZEND_WIN32
extern char accel_uname_id[32];
#endif
-extern zend_bool accel_startup_ok;
-extern zend_bool file_cache_only;
+extern bool accel_startup_ok;
+extern bool file_cache_only;
#if ENABLE_FILE_CACHE_FALLBACK
-extern zend_bool fallback_process;
+extern bool fallback_process;
#endif
extern zend_accel_shared_globals *accel_shared_globals;
@@ -322,7 +321,7 @@ void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason);
accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size);
int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle);
int validate_timestamp_and_record_ex(zend_persistent_script *persistent_script, zend_file_handle *file_handle);
-int zend_accel_invalidate(const char *filename, size_t filename_len, zend_bool force);
+int zend_accel_invalidate(const char *filename, size_t filename_len, bool force);
int accelerator_shm_read_lock(void);
void accelerator_shm_read_unlock(void);
diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4
index 93d72fb73d..33c99c59c1 100644
--- a/ext/opcache/config.m4
+++ b/ext/opcache/config.m4
@@ -312,31 +312,9 @@ int main() {
shared_alloc_shm.c \
shared_alloc_mmap.c \
shared_alloc_posix.c \
- Optimizer/zend_optimizer.c \
- Optimizer/pass1.c \
- Optimizer/pass3.c \
- Optimizer/optimize_func_calls.c \
- Optimizer/block_pass.c \
- Optimizer/optimize_temp_vars_5.c \
- Optimizer/nop_removal.c \
- Optimizer/compact_literals.c \
- Optimizer/zend_cfg.c \
- 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/sccp.c \
- Optimizer/scdf.c \
- Optimizer/dce.c \
- Optimizer/escape_analysis.c \
- Optimizer/compact_vars.c \
- Optimizer/zend_dump.c \
$ZEND_JIT_SRC,
shared,,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1,,yes)
- PHP_ADD_BUILD_DIR([$ext_builddir/Optimizer], 1)
PHP_ADD_EXTENSION_DEP(opcache, pcre)
if test "$have_shm_ipc" != "yes" && test "$have_shm_mmap_posix" != "yes" && test "$have_shm_mmap_anon" != "yes"; then
diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32
index fb921c73da..a7f292ee76 100644
--- a/ext/opcache/config.w32
+++ b/ext/opcache/config.w32
@@ -38,8 +38,6 @@ if (PHP_OPCACHE != "no") {
}
}
- ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1.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 escape_analysis.c compact_vars.c dce.c sccp.c scdf.c", "opcache", "ext\\opcache\\Optimizer");
-
ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname);
}
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index 1306bb36ba..ca3d9eccf0 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -124,14 +124,14 @@ static uint32_t zend_jit_trace_get_exit_point(const zend_op *to_opline, uint32_t
static const void *zend_jit_trace_get_exit_addr(uint32_t n);
static void zend_jit_trace_add_code(const void *start, uint32_t size);
-static zend_bool dominates(const zend_basic_block *blocks, int a, int b) {
+static bool dominates(const zend_basic_block *blocks, int a, int b) {
while (blocks[b].level > blocks[a].level) {
b = blocks[b].idom;
}
return a == b;
}
-static zend_bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use)
+static bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use)
{
int next_use;
@@ -165,7 +165,7 @@ static zend_bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_
return 0;
}
-static zend_bool zend_ival_is_last_use(const zend_lifetime_interval *ival, int use)
+static bool zend_ival_is_last_use(const zend_lifetime_interval *ival, int use)
{
if (ival->flags & ZREG_LAST_USE) {
const zend_life_range *range = &ival->range;
@@ -178,7 +178,7 @@ static zend_bool zend_ival_is_last_use(const zend_lifetime_interval *ival, int u
return 0;
}
-static zend_bool zend_is_commutative(zend_uchar opcode)
+static bool zend_is_commutative(zend_uchar opcode)
{
return
opcode == ZEND_ADD ||
@@ -188,7 +188,7 @@ static zend_bool zend_is_commutative(zend_uchar opcode)
opcode == ZEND_BW_XOR;
}
-static zend_bool zend_long_is_power_of_two(zend_long x)
+static bool zend_long_is_power_of_two(zend_long x)
{
return (x > 0) && !(x & (x - 1));
}
@@ -1044,7 +1044,7 @@ static int zend_jit_compute_block_order(zend_ssa *ssa, int *block_order)
return end - block_order;
}
-static zend_bool zend_jit_in_loop(zend_ssa *ssa, int header, zend_basic_block *b)
+static bool zend_jit_in_loop(zend_ssa *ssa, int header, zend_basic_block *b)
{
while (b->loop_header >= 0) {
if (b->loop_header == header) {
@@ -1435,7 +1435,7 @@ static uint32_t zend_interval_end(zend_lifetime_interval *ival)
return range->end;
}
-static zend_bool zend_interval_covers(zend_lifetime_interval *ival, uint32_t position)
+static bool zend_interval_covers(zend_lifetime_interval *ival, uint32_t position)
{
zend_life_range *range = &ival->range;
@@ -1997,7 +1997,7 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
((intervals[i]->flags & ZREG_LOAD) ||
((intervals[i]->flags & ZREG_STORE) && ssa->vars[i].definition >= 0)) &&
ssa->vars[i].use_chain < 0) {
- zend_bool may_remove = 1;
+ bool may_remove = 1;
zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
while (phi) {
@@ -2020,7 +2020,7 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
(intervals[i]->flags & ZREG_STORE) &&
(ssa->vars[i].use_chain < 0 ||
zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) {
- zend_bool may_remove = 1;
+ bool may_remove = 1;
zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
while (phi) {
@@ -2070,14 +2070,14 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
int call_level = 0;
void *checkpoint = NULL;
zend_lifetime_interval **ra = NULL;
- zend_bool is_terminated = 1; /* previous basic block is terminated by jump */
- zend_bool recv_emitted = 0; /* emitted at least one RECV opcode */
+ bool is_terminated = 1; /* previous basic block is terminated by jump */
+ bool recv_emitted = 0; /* emitted at least one RECV opcode */
zend_uchar smart_branch_opcode;
uint32_t target_label, target_label2;
uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info;
zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
zend_class_entry *ce;
- zend_bool ce_is_instanceof;
+ bool ce_is_instanceof;
if (JIT_G(bisect_limit)) {
jit_bisect_pos++;
@@ -2932,7 +2932,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
}
} else {
int j;
- zend_bool left_frame = 0;
+ bool left_frame = 0;
if (!zend_jit_return(&dasm_state, opline, op_array,
op1_info, OP1_REG_ADDR())) {
@@ -3629,7 +3629,7 @@ static void ZEND_FASTCALL zend_runtime_jit(void)
/* JIT-ed code is going to be called by VM */
}
-void zend_jit_check_funcs(HashTable *function_table, zend_bool is_method) {
+void zend_jit_check_funcs(HashTable *function_table, bool is_method) {
zend_op *opline;
zend_function *func;
zend_op_array *op_array;
@@ -4209,7 +4209,7 @@ ZEND_EXT_API int zend_jit_check_support(void)
return SUCCESS;
}
-ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, zend_bool reattached)
+ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, bool reattached)
{
int ret;
diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h
index fffac1edc2..0d71efa470 100644
--- a/ext/opcache/jit/zend_jit.h
+++ b/ext/opcache/jit/zend_jit.h
@@ -81,8 +81,8 @@ typedef struct _zend_jit_trace_stack_frame zend_jit_trace_stack_frame;
typedef struct _sym_node zend_sym_node;
typedef struct _zend_jit_globals {
- zend_bool enabled;
- zend_bool on;
+ bool enabled;
+ bool on;
uint8_t trigger;
uint8_t opt_level;
uint32_t opt_flags;
@@ -108,7 +108,7 @@ typedef struct _zend_jit_globals {
zend_sym_node *symbols; /* symbols for disassembler */
- zend_bool tracing;
+ bool tracing;
zend_jit_trace_rec *current_trace;
zend_jit_trace_stack_frame *current_frame;
@@ -137,7 +137,7 @@ ZEND_EXT_API void zend_jit_init(void);
ZEND_EXT_API int zend_jit_config(zend_string *jit_options, int stage);
ZEND_EXT_API int zend_jit_debug_config(zend_long old_val, zend_long new_val, int stage);
ZEND_EXT_API int zend_jit_check_support(void);
-ZEND_EXT_API int zend_jit_startup(void *jit_buffer, size_t size, zend_bool reattached);
+ZEND_EXT_API int zend_jit_startup(void *jit_buffer, size_t size, bool reattached);
ZEND_EXT_API void zend_jit_shutdown(void);
ZEND_EXT_API void zend_jit_activate(void);
ZEND_EXT_API void zend_jit_deactivate(void);
diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c
index 9fd4bddf15..696013f60f 100644
--- a/ext/opcache/jit/zend_jit_helpers.c
+++ b/ext/opcache/jit/zend_jit_helpers.c
@@ -272,18 +272,7 @@ static zval* ZEND_FASTCALL zend_jit_hash_index_lookup_w(HashTable *ht, zend_long
static zval* ZEND_FASTCALL zend_jit_hash_lookup_rw(HashTable *ht, zend_string *str)
{
zval *retval = zend_hash_find_ex(ht, str, 1);
-
- if (retval) {
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- if (UNEXPECTED(zend_undefined_index_write(ht, str) == FAILURE)) {
- return NULL;
- }
- ZVAL_NULL(retval);
- }
- }
- } else {
+ if (!retval) {
/* Key may be released while throwing the undefined index warning. */
zend_string_addref(str);
if (UNEXPECTED(zend_undefined_index_write(ht, str) == FAILURE)) {
@@ -299,15 +288,7 @@ static zval* ZEND_FASTCALL zend_jit_hash_lookup_rw(HashTable *ht, zend_string *s
static zval* ZEND_FASTCALL zend_jit_hash_lookup_w(HashTable *ht, zend_string *str)
{
zval *retval = zend_hash_find_ex(ht, str, 1);
-
- if (retval) {
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- ZVAL_NULL(retval);
- }
- }
- } else {
+ if (!retval) {
retval = zend_hash_add_new(ht, str, &EG(uninitialized_zval));
}
return retval;
@@ -344,17 +325,7 @@ static zval* ZEND_FASTCALL zend_jit_symtable_lookup_rw(HashTable *ht, zend_strin
} while (0);
retval = zend_hash_find(ht, str);
- if (retval) {
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- if (UNEXPECTED(zend_undefined_index_write(ht, str) == FAILURE)) {
- return NULL;
- }
- ZVAL_NULL(retval);
- }
- }
- } else {
+ if (!retval) {
/* Key may be released while throwing the undefined index warning. */
zend_string_addref(str);
if (UNEXPECTED(zend_undefined_index_write(ht, str) == FAILURE)) {
@@ -395,14 +366,7 @@ static zval* ZEND_FASTCALL zend_jit_symtable_lookup_w(HashTable *ht, zend_string
} while (0);
retval = zend_hash_find(ht, str);
- if (retval) {
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- ZVAL_NULL(retval);
- }
- }
- } else {
+ if (!retval) {
retval = zend_hash_add_new(ht, str, &EG(uninitialized_zval));
}
return retval;
@@ -482,17 +446,7 @@ str_index:
goto num_index;
}
retval = zend_hash_find(ht, offset_key);
- if (retval) {
- /* support for $GLOBALS[...] */
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
- ZVAL_NULL(result);
- return;
- }
- }
- } else {
+ if (!retval) {
zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
ZVAL_NULL(result);
return;
@@ -557,16 +511,7 @@ str_index:
goto num_index;
}
retval = zend_hash_find(ht, offset_key);
- if (retval) {
- /* support for $GLOBALS[...] */
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- ZVAL_NULL(result);
- return;
- }
- }
- } else {
+ if (!retval) {
ZVAL_NULL(result);
return;
}
@@ -628,18 +573,13 @@ str_index:
goto num_index;
}
retval = zend_hash_find(ht, offset_key);
- if (retval) {
- /* support for $GLOBALS[...] */
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- }
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_REFERENCE)) {
- retval = Z_REFVAL_P(retval);
- }
- return (Z_TYPE_P(retval) > IS_NULL);
- } else {
+ if (!retval) {
return 0;
}
+ if (UNEXPECTED(Z_TYPE_P(retval) == IS_REFERENCE)) {
+ retval = Z_REFVAL_P(retval);
+ }
+ return Z_TYPE_P(retval) > IS_NULL;
num_index:
ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
@@ -701,18 +641,7 @@ str_index:
goto num_index;
}
retval = zend_hash_find(ht, offset_key);
- if (retval) {
- /* support for $GLOBALS[...] */
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- if (UNEXPECTED(zend_undefined_index_write(ht, offset_key) == FAILURE)) {
- return NULL;
- }
- ZVAL_NULL(retval);
- }
- }
- } else {
+ if (!retval) {
/* Key may be released while throwing the undefined index warning. */
zend_string_addref(offset_key);
if (UNEXPECTED(zend_undefined_index_write(ht, offset_key) == FAILURE)) {
@@ -785,15 +714,7 @@ str_index:
goto num_index;
}
retval = zend_hash_find(ht, offset_key);
- if (retval) {
- /* support for $GLOBALS[...] */
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) {
- retval = Z_INDIRECT_P(retval);
- if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
- ZVAL_NULL(retval);
- }
- }
- } else {
+ if (!retval) {
retval = zend_hash_add_new(ht, offset_key, &EG(uninitialized_zval));
}
return retval;
@@ -1452,7 +1373,7 @@ check_indirect:
return ref;
}
-static zend_always_inline zend_bool zend_jit_verify_type_common(zval *arg, zend_arg_info *arg_info, void **cache_slot)
+static zend_always_inline bool zend_jit_verify_type_common(zval *arg, zend_arg_info *arg_info, void **cache_slot)
{
uint32_t type_mask;
@@ -1510,12 +1431,12 @@ builtin_types:
return 0;
}
-static zend_bool ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_arg_info *arg_info)
+static bool ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_arg_info *arg_info)
{
zend_execute_data *execute_data = EG(current_execute_data);
const zend_op *opline = EX(opline);
void **cache_slot = CACHE_ADDR(opline->extended_value);
- zend_bool ret;
+ bool ret;
ret = zend_jit_verify_type_common(arg, arg_info, cache_slot);
if (UNEXPECTED(!ret)) {
@@ -1648,12 +1569,12 @@ static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intpt
zend_jit_fetch_obj_is_slow(zobj);
}
-static zend_always_inline zend_bool promotes_to_array(zval *val) {
+static zend_always_inline bool promotes_to_array(zval *val) {
return Z_TYPE_P(val) <= IS_FALSE
|| (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE);
}
-static zend_always_inline zend_bool check_type_array_assignable(zend_type type) {
+static zend_always_inline bool check_type_array_assignable(zend_type type) {
if (!ZEND_TYPE_IS_SET(type)) {
return 1;
}
@@ -1695,7 +1616,7 @@ static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_err
zend_get_unmangled_property_name(prop->name));
}
-static zend_never_inline zend_bool zend_handle_fetch_obj_flags(
+static zend_never_inline bool zend_handle_fetch_obj_flags(
zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags)
{
switch (flags) {
@@ -1878,7 +1799,7 @@ static zend_property_info *zend_jit_get_prop_not_accepting_double(zend_reference
return NULL;
}
-static ZEND_COLD void zend_jit_throw_incdec_ref_error(zend_reference *ref, zend_bool inc)
+static ZEND_COLD void zend_jit_throw_incdec_ref_error(zend_reference *ref, bool inc)
{
zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
/* Currently there should be no way for a typed reference to accept both int and double.
diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h
index 917fa10f2e..906262773f 100644
--- a/ext/opcache/jit/zend_jit_internal.h
+++ b/ext/opcache/jit/zend_jit_internal.h
@@ -128,7 +128,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_func_counter_helper(ZEND_OPCODE_H
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_counter_helper(ZEND_OPCODE_HANDLER_ARGS);
void ZEND_FASTCALL zend_jit_copy_extra_args_helper(EXECUTE_DATA_D);
-zend_bool ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D);
+bool ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D);
zend_constant* ZEND_FASTCALL zend_jit_get_constant(const zval *key, uint32_t flags);
zend_constant* ZEND_FASTCALL zend_jit_check_constant(const zval *key);
@@ -484,7 +484,7 @@ int ZEND_FASTCALL zend_jit_trace_hot_root(zend_execute_data *execute_data, const
int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf *regs);
zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *execute_data, const zend_op *opline, zend_jit_trace_rec *trace_buffer, uint8_t start, uint32_t is_megamorphc);
-static zend_always_inline const zend_op* zend_jit_trace_get_exit_opline(zend_jit_trace_rec *trace, const zend_op *opline, zend_bool *exit_if_true)
+static zend_always_inline const zend_op* zend_jit_trace_get_exit_opline(zend_jit_trace_rec *trace, const zend_op *opline, bool *exit_if_true)
{
if (trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END) {
if (trace->opline == opline + 1) {
@@ -505,7 +505,7 @@ static zend_always_inline const zend_op* zend_jit_trace_get_exit_opline(zend_jit
return NULL;
}
-static zend_always_inline zend_bool zend_jit_may_be_polymorphic_call(const zend_op *opline)
+static zend_always_inline bool zend_jit_may_be_polymorphic_call(const zend_op *opline)
{
if (opline->opcode == ZEND_INIT_FCALL
|| opline->opcode == ZEND_INIT_FCALL_BY_NAME
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 69c4f79fa8..d0ca0e911d 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -2423,7 +2423,7 @@ static zend_lifetime_interval** zend_jit_trace_allocate_registers(zend_jit_trace
if (p->op == ZEND_JIT_TRACE_VM) {
const zend_op *opline = p->opline;
int len;
- zend_bool support_opline;
+ bool support_opline;
support_opline =
zend_jit_opline_supports_reg(op_array, ssa, opline, ssa_op, p);
@@ -3085,7 +3085,7 @@ static void zend_jit_trace_setup_ret_counter(const zend_op *opline, size_t offse
}
}
-static zend_bool zend_jit_may_delay_fetch_this(zend_ssa *ssa, const zend_op **ssa_opcodes, int var)
+static bool zend_jit_may_delay_fetch_this(zend_ssa *ssa, const zend_op **ssa_opcodes, int var)
{
int i;
int use = ssa->vars[var].use_chain;
@@ -3176,11 +3176,11 @@ static int zend_jit_trace_deoptimization(dasm_State **Dst,
zend_ssa *ssa,
zend_jit_trace_stack *stack,
zend_lifetime_interval **ra,
- zend_bool polymorphic_side_trace)
+ bool polymorphic_side_trace)
{
int i;
- zend_bool has_constants = 0;
- zend_bool has_unsaved_vars = 0;
+ bool has_constants = 0;
+ bool has_unsaved_vars = 0;
// TODO: Merge this loop with the following register LOAD loop to implement parallel move ???
for (i = 0; i < parent_vars_count; i++) {
@@ -3317,7 +3317,7 @@ static void zend_jit_trace_set_var_range(zend_ssa_var_info *info, zend_long min,
info->range.overflow = 0;
}
-static void zend_jit_trace_update_condition_ranges(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, zend_bool exit_if_true)
+static void zend_jit_trace_update_condition_ranges(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, bool exit_if_true)
{
zend_long op1_min, op1_max, op2_min, op2_max;
@@ -3435,7 +3435,7 @@ static void zend_jit_trace_update_condition_ranges(const zend_op *opline, const
}
}
-static zend_bool zend_jit_may_skip_comparison(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_ssa *ssa, const zend_op **ssa_opcodes)
+static bool zend_jit_may_skip_comparison(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_ssa *ssa, const zend_op **ssa_opcodes)
{
zend_uchar prev_opcode;
@@ -3537,14 +3537,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
zend_uchar smart_branch_opcode;
const void *exit_addr;
uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_data_info;
- zend_bool send_result = 0;
- zend_bool skip_comparison;
+ bool send_result = 0;
+ bool skip_comparison;
zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
zend_class_entry *ce;
- zend_bool ce_is_instanceof;
- zend_bool delayed_fetch_this = 0;
- zend_bool avoid_refcounting = 0;
- zend_bool polymorphic_side_trace =
+ bool ce_is_instanceof;
+ bool delayed_fetch_this = 0;
+ bool avoid_refcounting = 0;
+ bool polymorphic_side_trace =
parent_trace &&
(zend_jit_traces[parent_trace].exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL);
uint32_t i;
@@ -3817,7 +3817,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
uint8_t op3_type = p->op3_type;
uint8_t orig_op1_type = op1_type;
uint8_t orig_op2_type = op2_type;
- zend_bool op1_indirect;
+ bool op1_indirect;
zend_class_entry *op1_ce = NULL;
zend_class_entry *op2_ce = NULL;
@@ -4773,7 +4773,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
CHECK_OP1_TRACE_TYPE();
CHECK_OP2_TRACE_TYPE();
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
uint32_t exit_point;
@@ -4821,7 +4821,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
CHECK_OP1_TRACE_TYPE();
CHECK_OP2_TRACE_TYPE();
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
uint32_t exit_point;
@@ -4861,7 +4861,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
goto done;
case ZEND_DEFINED:
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
uint32_t exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
@@ -4885,7 +4885,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
}
op1_info = OP1_INFO();
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
uint32_t exit_point;
@@ -4925,7 +4925,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
} else {
int j;
int may_throw = 0;
- zend_bool left_frame = 0;
+ bool left_frame = 0;
if (!zend_jit_return(&dasm_state, opline, op_array,
op1_info, OP1_REG_ADDR())) {
@@ -5081,7 +5081,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
CHECK_OP1_TRACE_TYPE();
}
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
uint32_t exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
@@ -5111,7 +5111,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
break;
}
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
uint32_t exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
@@ -5274,7 +5274,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
op2_info = OP2_INFO();
CHECK_OP2_TRACE_TYPE();
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
uint32_t exit_point;
@@ -6408,7 +6408,7 @@ static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_n
const zend_op *opline;
uint32_t stack_size;
zend_jit_trace_stack *stack;
- zend_bool original_handler = 0;
+ bool original_handler = 0;
if (!zend_jit_trace_exit_needs_deoptimization(trace_num, exit_num)) {
return dasm_labels[zend_lbtrace_escape];
@@ -6589,7 +6589,7 @@ static void zend_jit_blacklist_root_trace(const zend_op *opline, size_t offset)
zend_shared_alloc_unlock();
}
-static zend_bool zend_jit_trace_is_bad_root(const zend_op *opline, zend_jit_trace_stop stop, size_t offset)
+static bool zend_jit_trace_is_bad_root(const zend_op *opline, zend_jit_trace_stop stop, size_t offset)
{
const zend_op **cache_opline = JIT_G(bad_root_cache_opline);
uint8_t *cache_count = JIT_G(bad_root_cache_count);
@@ -7054,7 +7054,7 @@ static void zend_jit_blacklist_trace_exit(uint32_t trace_num, uint32_t exit_num)
zend_shared_alloc_unlock();
}
-static zend_bool zend_jit_trace_exit_is_bad(uint32_t trace_num, uint32_t exit_num)
+static bool zend_jit_trace_exit_is_bad(uint32_t trace_num, uint32_t exit_num)
{
uint8_t *counter = JIT_G(exit_counters) +
zend_jit_traces[trace_num].exit_counters + exit_num;
@@ -7066,7 +7066,7 @@ static zend_bool zend_jit_trace_exit_is_bad(uint32_t trace_num, uint32_t exit_nu
return 0;
}
-static zend_bool zend_jit_trace_exit_is_hot(uint32_t trace_num, uint32_t exit_num)
+static bool zend_jit_trace_exit_is_hot(uint32_t trace_num, uint32_t exit_num)
{
uint8_t *counter = JIT_G(exit_counters) +
zend_jit_traces[trace_num].exit_counters + exit_num;
diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c
index d919b59c9e..9a7b205340 100644
--- a/ext/opcache/jit/zend_jit_vm_helpers.c
+++ b/ext/opcache/jit/zend_jit_vm_helpers.c
@@ -161,7 +161,7 @@ void ZEND_FASTCALL zend_jit_copy_extra_args_helper(EXECUTE_DATA_D)
}
}
-zend_bool ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D)
+bool ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D)
{
zend_execute_data *call = (zend_execute_data *) opline;
zend_function *fbc = call->func;
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 93e95886aa..5700a92ff1 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -1628,12 +1628,12 @@ static void* dasm_labels[zend_lb_MAX];
|| }
|.endmacro
-static zend_bool reuse_ip = 0;
-static zend_bool delayed_call_chain = 0;
+static bool reuse_ip = 0;
+static bool delayed_call_chain = 0;
static uint32_t delayed_call_level = 0;
static const zend_op *last_valid_opline = NULL;
-static zend_bool use_last_vald_opline = 0;
-static zend_bool track_last_valid_opline = 0;
+static bool use_last_vald_opline = 0;
+static bool track_last_valid_opline = 0;
static int jit_return_label = -1;
static uint32_t current_trace_num = 0;
static uint32_t allowed_opt_flags = 0;
@@ -1652,7 +1652,7 @@ static void zend_jit_use_last_valid_opline(void)
}
}
-static zend_bool zend_jit_trace_uses_initial_ip(void)
+static bool zend_jit_trace_uses_initial_ip(void)
{
return use_last_vald_opline;
}
@@ -1716,12 +1716,12 @@ static uint32_t floor_log2(uint32_t x)
return ones32(x) - 1;
}
-static zend_bool is_power_of_two(uint32_t x)
+static bool is_power_of_two(uint32_t x)
{
return !(x & (x - 1)) && x != 0;
}
-static zend_bool has_concrete_type(uint32_t value_type)
+static bool has_concrete_type(uint32_t value_type)
{
return is_power_of_two (value_type & (MAY_BE_ANY|MAY_BE_UNDEF));
}
@@ -1731,7 +1731,7 @@ static uint32_t concrete_type(uint32_t value_type)
return floor_log2(value_type & (MAY_BE_ANY|MAY_BE_UNDEF));
}
-static inline zend_bool is_signed(double d)
+static inline bool is_signed(double d)
{
return (((unsigned char*)&d)[sizeof(double)-1] & 0x80) != 0;
}
@@ -2651,7 +2651,7 @@ static int zend_jit_assign_to_variable(dasm_State **Dst,
zend_jit_addr val_addr,
uint32_t val_info,
zend_jit_addr res_addr,
- zend_bool check_exception);
+ bool check_exception);
static int zend_jit_assign_const_stub(dasm_State **Dst)
{
@@ -3397,7 +3397,7 @@ static int zend_jit_trace_link_to_root(dasm_State **Dst, zend_jit_trace_info *t,
return 1;
}
-static int zend_jit_trace_return(dasm_State **Dst, zend_bool original_handler)
+static int zend_jit_trace_return(dasm_State **Dst, bool original_handler)
{
#if 0
| jmp ->trace_escape
@@ -3549,7 +3549,7 @@ static int zend_jit_trace_handler(dasm_State **Dst, const zend_op_array *op_arra
zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
if (zend_is_smart_branch(opline)) {
- zend_bool exit_if_true = 0;
+ bool exit_if_true = 0;
exit_opline = zend_jit_trace_get_exit_opline(trace, opline + 1, &exit_if_true);
} else {
switch (opline->opcode) {
@@ -3756,7 +3756,7 @@ static int zend_jit_call(dasm_State **Dst, const zend_op *opline, unsigned int n
#endif
}
-static int zend_jit_spill_store(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info, zend_bool set_type)
+static int zend_jit_spill_store(dasm_State **Dst, zend_jit_addr src, zend_jit_addr dst, uint32_t info, bool set_type)
{
ZEND_ASSERT(Z_MODE(src) == IS_REG);
ZEND_ASSERT(Z_MODE(dst) == IS_MEM_ZVAL);
@@ -3792,7 +3792,7 @@ static int zend_jit_load_reg(dasm_State **Dst, zend_jit_addr src, zend_jit_addr
return 1;
}
-static int zend_jit_store_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg, zend_bool set_type)
+static int zend_jit_store_var(dasm_State **Dst, uint32_t info, int var, zend_reg reg, bool set_type)
{
zend_jit_addr src = ZEND_ADDR_REG(reg);
zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
@@ -3813,7 +3813,7 @@ static int zend_jit_store_var_if_necessary_ex(dasm_State **Dst, int var, zend_ji
{
if (Z_MODE(src) == IS_REG && Z_STORE(src)) {
zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var);
- zend_bool set_type = 1;
+ bool set_type = 1;
if ((info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) ==
(old_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF))) {
@@ -4210,7 +4210,7 @@ static int zend_jit_math_long_long(dasm_State **Dst,
uint32_t res_use_info,
int may_overflow)
{
- zend_bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+ bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
zend_reg result_reg;
zend_reg tmp_reg = ZREG_R0;
@@ -4520,7 +4520,7 @@ static int zend_jit_math_double_double(dasm_State **Dst,
zend_jit_addr res_addr,
uint32_t res_use_info)
{
- zend_bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+ bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
zend_reg result_reg;
if (Z_MODE(res_addr) == IS_REG) {
@@ -4611,7 +4611,7 @@ static int zend_jit_math_helper(dasm_State **Dst,
int may_throw)
/* Labels: 1,2,3,4,5,6 */
{
- zend_bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+ bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
if (op1_info & (MAY_BE_ANY-MAY_BE_LONG)) {
@@ -4864,7 +4864,7 @@ static int zend_jit_long_math_helper(dasm_State **Dst,
int may_throw)
/* Labels: 6 */
{
- zend_bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
+ bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
zend_reg result_reg;
zval tmp;
@@ -5283,8 +5283,8 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
}
if (op2_info & MAY_BE_LONG) {
- zend_bool op2_loaded = 0;
- zend_bool packed_loaded = 0;
+ bool op2_loaded = 0;
+ bool packed_loaded = 0;
if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) {
| // if (EXPECTED(Z_TYPE_P(dim) == IS_LONG))
@@ -5572,10 +5572,6 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
} else {
| jz >9 // NOT_FOUND
}
- | // if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT))
- | IF_NOT_Z_TYPE r0, IS_INDIRECT, >1
- | GET_Z_PTR r0, r0
- |1:
break;
case BP_VAR_R:
case BP_VAR_IS:
@@ -5602,40 +5598,25 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
| jz >7 // NOT_FOUND
} else {
| jz >2 // NOT_FOUND
- }
- | // if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT))
- | IF_Z_TYPE r0, IS_INDIRECT, >1 // SLOW
- |.cold_code
- |1:
- | // retval = Z_INDIRECT_P(retval);
- | GET_Z_PTR r0, r0
- | IF_NOT_Z_TYPE r0, IS_UNDEF, >8
- if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
- | jmp &exit_addr
- } else if (type == BP_VAR_IS && not_found_exit_addr) {
- | jmp &not_found_exit_addr
- }
- |2:
- switch (type) {
- case BP_VAR_R:
- if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) {
+ |.cold_code
+ |2:
+ switch (type) {
+ case BP_VAR_R:
// zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
| UNDEFINED_INDEX opline
| jmp >9
- }
- break;
- case BP_VAR_IS:
- case BP_VAR_UNSET:
- if (!not_found_exit_addr && !found_exit_addr) {
+ break;
+ case BP_VAR_IS:
+ case BP_VAR_UNSET:
| // retval = &EG(uninitialized_zval);
| SET_ZVAL_TYPE_INFO res_addr, IS_NULL
| jmp >9
- }
- break;
- default:
- ZEND_UNREACHABLE();
+ break;
+ default:
+ ZEND_UNREACHABLE();
+ }
+ |.code
}
- |.code
break;
case BP_VAR_RW:
| SET_EX_OPLINE opline, r0
@@ -5904,7 +5885,7 @@ static int zend_jit_assign_to_typed_ref(dasm_State **Dst,
const zend_op *opline,
zend_uchar val_type,
zend_jit_addr val_addr,
- zend_bool check_exception)
+ bool check_exception)
{
| // if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
| cmp aword [FCARG1a + offsetof(zend_reference, sources.ptr)], 0
@@ -5951,7 +5932,7 @@ static int zend_jit_assign_to_variable_call(dasm_State **Dst,
zend_jit_addr val_addr,
uint32_t val_info,
zend_jit_addr __res_addr,
- zend_bool __check_exception)
+ bool __check_exception)
{
if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1a || Z_OFFSET(var_addr) != 0) {
| LOAD_ZVAL_ADDR FCARG1a, var_addr
@@ -5997,7 +5978,7 @@ static int zend_jit_assign_to_variable(dasm_State **Dst,
zend_jit_addr val_addr,
uint32_t val_info,
zend_jit_addr res_addr,
- zend_bool check_exception)
+ bool check_exception)
/* Labels: 1,2,3,4,5,8 */
{
int done = 0;
@@ -6038,7 +6019,7 @@ static int zend_jit_assign_to_variable(dasm_State **Dst,
in_cold = 1;
}
if (Z_REG(var_use_addr) == ZREG_FCARG1a || Z_REG(var_use_addr) == ZREG_R0) {
- zend_bool keep_gc = 0;
+ bool keep_gc = 0;
| GET_ZVAL_PTR Ra(tmp_reg), var_use_addr
if (tmp_reg == ZREG_FCARG1a) {
@@ -6661,7 +6642,7 @@ static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
zend_jit_addr op1_addr,
zend_ssa_range *op2_range,
zend_jit_addr op2_addr,
- zend_bool *result)
+ bool *result)
{
zend_long op1_min;
zend_long op1_max;
@@ -6746,10 +6727,10 @@ static int zend_jit_cmp_long_long(dasm_State **Dst,
uint32_t target_label,
uint32_t target_label2,
const void *exit_addr,
- zend_bool skip_comparison)
+ bool skip_comparison)
{
- zend_bool swap = 0;
- zend_bool result;
+ bool swap = 0;
+ bool result;
if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) {
if (!smart_branch_opcode ||
@@ -7040,7 +7021,7 @@ static int zend_jit_cmp_long_long(dasm_State **Dst,
return 1;
}
-static int zend_jit_cmp_double_common(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, zend_bool swap, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+static int zend_jit_cmp_double_common(dasm_State **Dst, const zend_op *opline, zend_jit_addr res_addr, bool swap, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
{
if (smart_branch_opcode) {
if (smart_branch_opcode == ZEND_JMPZ) {
@@ -7399,7 +7380,7 @@ static int zend_jit_cmp_double_long(dasm_State **Dst, const zend_op *opline, zen
static int zend_jit_cmp_double_double(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
{
- zend_bool swap = 0;
+ bool swap = 0;
if (Z_MODE(op1_addr) == IS_REG) {
| SSE_AVX_OP ucomisd, vucomisd, Z_REG(op1_addr), op2_addr
@@ -7575,10 +7556,10 @@ static int zend_jit_cmp(dasm_State **Dst,
uint32_t target_label,
uint32_t target_label2,
const void *exit_addr,
- zend_bool skip_comparison)
+ bool skip_comparison)
{
- zend_bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
- zend_bool has_slow;
+ bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
+ bool has_slow;
has_slow =
(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) &&
@@ -7814,7 +7795,7 @@ static int zend_jit_identical(dasm_State **Dst,
uint32_t target_label,
uint32_t target_label2,
const void *exit_addr,
- zend_bool skip_comparison)
+ bool skip_comparison)
{
uint32_t identical_label = (uint32_t)-1;
uint32_t not_identical_label = (uint32_t)-1;
@@ -8190,10 +8171,10 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_
{
uint32_t true_label = -1;
uint32_t false_label = -1;
- zend_bool set_bool = 0;
- zend_bool set_bool_not = 0;
- zend_bool set_delayed = 0;
- zend_bool jmp_done = 0;
+ bool set_bool = 0;
+ bool set_bool_not = 0;
+ bool set_delayed = 0;
+ bool jmp_done = 0;
if (branch_opcode == ZEND_BOOL) {
set_bool = 1;
@@ -8734,7 +8715,7 @@ static int zend_jit_stack_check(dasm_State **Dst, const zend_op *opline, uint32_
return 1;
}
-static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_function *func, zend_bool is_closure, zend_bool use_this, zend_bool stack_check)
+static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_function *func, bool is_closure, bool use_this, bool stack_check)
{
uint32_t used_stack;
@@ -9172,7 +9153,7 @@ static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zen
return 1;
}
-static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace, zend_bool stack_check)
+static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, int call_level, zend_jit_trace_rec *trace, bool stack_check)
{
zend_func_info *info = ZEND_FUNC_INFO(op_array);
zend_call_info *call_info = NULL;
@@ -9334,12 +9315,12 @@ static int zend_jit_init_method_call(dasm_State **Dst,
uint32_t op1_info,
zend_jit_addr op1_addr,
zend_class_entry *ce,
- zend_bool ce_is_instanceof,
- zend_bool use_this,
+ bool ce_is_instanceof,
+ bool use_this,
zend_class_entry *trace_ce,
zend_jit_trace_rec *trace,
- zend_bool stack_check,
- zend_bool polymorphic_side_trace)
+ bool stack_check,
+ bool polymorphic_side_trace)
{
zend_func_info *info = ZEND_FUNC_INFO(op_array);
zend_call_info *call_info = NULL;
@@ -9583,7 +9564,7 @@ static int zend_jit_init_closure_call(dasm_State **Dst,
const zend_ssa_op *ssa_op,
int call_level,
zend_jit_trace_rec *trace,
- zend_bool stack_check)
+ bool stack_check)
{
zend_function *func = NULL;
zend_jit_addr op2_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
@@ -9709,7 +9690,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
uint32_t i;
zend_jit_addr res_addr;
uint32_t call_num_args = 0;
- zend_bool unknown_num_args = 0;
+ bool unknown_num_args = 0;
const void *exit_addr = NULL;
const zend_op *prev_opline;
@@ -10239,7 +10220,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
if (!RETURN_VALUE_USED(opline)) {
zend_class_entry *ce;
- zend_bool ce_is_instanceof;
+ bool ce_is_instanceof;
uint32_t func_info = call_info ?
zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof) :
(MAY_BE_ANY|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
@@ -11185,17 +11166,17 @@ static int zend_jit_leave_func(dasm_State **Dst,
const zend_op_array *op_array,
const zend_op *opline,
uint32_t op1_info,
- zend_bool left_frame,
+ bool left_frame,
zend_jit_trace_rec *trace,
zend_jit_trace_info *trace_info,
int indirect_var_access,
int may_throw)
{
- zend_bool may_be_top_frame =
+ bool may_be_top_frame =
JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE ||
!JIT_G(current_frame) ||
!TRACE_FRAME_IS_NESTED(JIT_G(current_frame));
- zend_bool may_need_call_helper =
+ bool may_need_call_helper =
indirect_var_access || /* may have symbol table */
!op_array->function_name || /* may have symbol table */
may_be_top_frame ||
@@ -11204,7 +11185,7 @@ static int zend_jit_leave_func(dasm_State **Dst,
!JIT_G(current_frame) ||
TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) == -1 || /* unknown number of args */
(uint32_t)TRACE_FRAME_NUM_ARGS(JIT_G(current_frame)) > op_array->num_args; /* extra args */
- zend_bool may_need_release_this =
+ bool may_need_release_this =
!(op_array->fn_flags & ZEND_ACC_CLOSURE) &&
op_array->scope &&
!(op_array->fn_flags & ZEND_ACC_STATIC) &&
@@ -11618,7 +11599,7 @@ static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, ze
return 1;
}
-static zend_bool zend_jit_may_avoid_refcounting(const zend_op *opline)
+static bool zend_jit_may_avoid_refcounting(const zend_op *opline)
{
switch (opline->opcode) {
case ZEND_FETCH_OBJ_FUNC_ARG:
@@ -11661,7 +11642,7 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst,
const zend_ssa_op *ssa_op,
uint32_t op1_info,
zend_jit_addr op1_addr,
- zend_bool op1_avoid_refcounting,
+ bool op1_avoid_refcounting,
uint32_t op2_info,
uint32_t res_info,
zend_jit_addr res_addr,
@@ -11671,7 +11652,7 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst,
const void *exit_addr = NULL;
const void *not_found_exit_addr = NULL;
const void *res_exit_addr = NULL;
- zend_bool result_avoid_refcounting = 0;
+ bool result_avoid_refcounting = 0;
uint32_t may_be_string = (opline->opcode != ZEND_FETCH_LIST_R) ? MAY_BE_STRING : 0;
orig_op1_addr = OP1_ADDR();
@@ -12172,7 +12153,7 @@ static int zend_jit_isset_isempty_dim(dasm_State **Dst,
const zend_op *opline,
uint32_t op1_info,
zend_jit_addr op1_addr,
- zend_bool op1_avoid_refcounting,
+ bool op1_avoid_refcounting,
uint32_t op2_info,
int may_throw,
zend_uchar smart_branch_opcode,
@@ -12430,10 +12411,10 @@ static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_
return 1;
}
-static int zend_jit_verify_arg_type(dasm_State **Dst, const zend_op *opline, zend_arg_info *arg_info, zend_bool check_exception)
+static int zend_jit_verify_arg_type(dasm_State **Dst, const zend_op *opline, zend_arg_info *arg_info, bool check_exception)
{
zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
- zend_bool in_cold = 0;
+ bool in_cold = 0;
uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
zend_reg tmp_reg = (type_mask == 0 || is_power_of_two(type_mask)) ? ZREG_FCARG1a : ZREG_R0;
@@ -12554,7 +12535,7 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_
return 1;
}
-static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_bool is_last, int may_throw)
+static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool is_last, int may_throw)
{
uint32_t arg_num = opline->op1.num;
zval *zv = RT_CONSTANT(opline, opline->op2);
@@ -12630,7 +12611,7 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zen
return 1;
}
-static zend_property_info* zend_get_known_property_info(zend_class_entry *ce, zend_string *member, zend_bool on_this, zend_string *filename)
+static zend_property_info* zend_get_known_property_info(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
{
zend_property_info *info = NULL;
@@ -12682,7 +12663,7 @@ static zend_property_info* zend_get_known_property_info(zend_class_entry *ce, ze
return info;
}
-static zend_bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, zend_bool on_this, zend_string *filename)
+static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
{
zend_property_info *info;
@@ -12743,17 +12724,17 @@ static int zend_jit_fetch_obj(dasm_State **Dst,
const zend_ssa_op *ssa_op,
uint32_t op1_info,
zend_jit_addr op1_addr,
- zend_bool op1_indirect,
+ bool op1_indirect,
zend_class_entry *ce,
- zend_bool ce_is_instanceof,
- zend_bool use_this,
- zend_bool op1_avoid_refcounting,
+ bool ce_is_instanceof,
+ bool use_this,
+ bool op1_avoid_refcounting,
zend_class_entry *trace_ce,
int may_throw)
{
zval *member;
zend_property_info *prop_info;
- zend_bool may_be_dynamic = 1;
+ bool may_be_dynamic = 1;
zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
zend_jit_addr prop_addr;
@@ -12954,7 +12935,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst,
ssa->var_info[ssa_op->result_def].indirect_reference = 1;
}
} else {
- zend_bool result_avoid_refcounting = 0;
+ bool result_avoid_refcounting = 0;
if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) {
uint32_t flags = 0;
@@ -13134,10 +13115,10 @@ static int zend_jit_incdec_obj(dasm_State **Dst,
const zend_ssa_op *ssa_op,
uint32_t op1_info,
zend_jit_addr op1_addr,
- zend_bool op1_indirect,
+ bool op1_indirect,
zend_class_entry *ce,
- zend_bool ce_is_instanceof,
- zend_bool use_this,
+ bool ce_is_instanceof,
+ bool use_this,
zend_class_entry *trace_ce,
int may_throw)
{
@@ -13147,7 +13128,7 @@ static int zend_jit_incdec_obj(dasm_State **Dst,
zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
zend_jit_addr res_addr = 0;
zend_jit_addr prop_addr;
- zend_bool needs_slow_path = 0;
+ bool needs_slow_path = 0;
ZEND_ASSERT(opline->op2_type == IS_CONST);
ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
@@ -13510,10 +13491,10 @@ static int zend_jit_assign_obj_op(dasm_State **Dst,
zend_jit_addr op1_addr,
uint32_t val_info,
zend_ssa_range *val_range,
- zend_bool op1_indirect,
+ bool op1_indirect,
zend_class_entry *ce,
- zend_bool ce_is_instanceof,
- zend_bool use_this,
+ bool ce_is_instanceof,
+ bool use_this,
zend_class_entry *trace_ce,
int may_throw)
{
@@ -13523,7 +13504,7 @@ static int zend_jit_assign_obj_op(dasm_State **Dst,
zend_jit_addr val_addr = OP1_DATA_ADDR();
zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
zend_jit_addr prop_addr;
- zend_bool needs_slow_path = 0;
+ bool needs_slow_path = 0;
binary_op_type binary_op = get_binary_op(opline->extended_value);
ZEND_ASSERT(opline->op2_type == IS_CONST);
@@ -13840,10 +13821,10 @@ static int zend_jit_assign_obj(dasm_State **Dst,
uint32_t op1_info,
zend_jit_addr op1_addr,
uint32_t val_info,
- zend_bool op1_indirect,
+ bool op1_indirect,
zend_class_entry *ce,
- zend_bool ce_is_instanceof,
- zend_bool use_this,
+ bool ce_is_instanceof,
+ bool use_this,
zend_class_entry *trace_ce,
int may_throw)
{
@@ -13854,7 +13835,7 @@ static int zend_jit_assign_obj(dasm_State **Dst,
zend_jit_addr res_addr = 0;
zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This));
zend_jit_addr prop_addr;
- zend_bool needs_slow_path = 0;
+ bool needs_slow_path = 0;
if (RETURN_VALUE_USED(opline)) {
res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
@@ -14271,7 +14252,7 @@ static int zend_jit_load_this(dasm_State **Dst, uint32_t var)
return 1;
}
-static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_bool check_only)
+static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, bool check_only)
{
if (!op_array->scope || (op_array->fn_flags & ZEND_ACC_STATIC)) {
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
@@ -14679,13 +14660,13 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o
return 1;
}
-static zend_bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info)
+static bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info)
{
zend_arg_info *arg_info = &op_array->arg_info[-1];
ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
zend_jit_addr op1_addr = OP1_ADDR();
- zend_bool needs_slow_check = 1;
- zend_bool slow_check_in_cold = 1;
+ bool needs_slow_check = 1;
+ bool slow_check_in_cold = 1;
uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_ANY;
if (type_mask == 0) {
@@ -14848,21 +14829,21 @@ static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t o
| // array = EX_VAR(opline->op1.var);
| // fe_ht = Z_ARRVAL_P(array);
- | GET_ZVAL_PTR FCARG2a, op1_addr
+ | GET_ZVAL_PTR FCARG1a, op1_addr
| // pos = Z_FE_POS_P(array);
- | mov FCARG1d, dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)]
+ | mov eax, dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)]
| // p = fe_ht->arData + pos;
|.if X64
|| ZEND_ASSERT(sizeof(Bucket) == 32);
- | mov eax, FCARG1d
- | shl r0, 5
+ | mov FCARG2d, eax
+ | shl FCARG2a, 5
|.else
- | imul r0, FCARG1a, sizeof(Bucket)
+ | imul FCARG2a, r0, sizeof(Bucket)
|.endif
- | add r0, aword [FCARG2a + offsetof(zend_array, arData)]
+ | add FCARG2a, aword [FCARG1a + offsetof(zend_array, arData)]
|1:
| // if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
- | cmp dword [FCARG2a + offsetof(zend_array, nNumUsed)], FCARG1d
+ | cmp dword [FCARG1a + offsetof(zend_array, nNumUsed)], eax
| // ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
| // ZEND_VM_CONTINUE();
if (exit_addr) {
@@ -14875,28 +14856,16 @@ static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t o
| jbe =>target_label
}
| // pos++;
- | add FCARG1d, 1
+ | add eax, 1
| // value_type = Z_TYPE_INFO_P(value);
| // if (EXPECTED(value_type != IS_UNDEF)) {
- | IF_Z_TYPE r0, IS_UNDEF, >2
if (!exit_addr || exit_opcode == ZEND_JMP) {
- | IF_NOT_Z_TYPE r0, IS_INDIRECT, >3
+ | IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, >3
} else {
- | IF_NOT_Z_TYPE r0, IS_INDIRECT, &exit_addr
+ | IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, &exit_addr
}
- | // value = Z_INDIRECT_P(value);
- | GET_Z_PTR FCARG2a, r0
- | // value_type = Z_TYPE_INFO_P(value);
- | // if (EXPECTED(value_type != IS_UNDEF)) {
- if (!exit_addr || exit_opcode == ZEND_JMP) {
- | IF_NOT_Z_TYPE FCARG2a, IS_UNDEF, >4
- } else {
- | IF_NOT_Z_TYPE r0, IS_UNDEF, &exit_addr
- }
- | GET_ZVAL_PTR FCARG2a, op1_addr // reload
- |2:
| // p++;
- | add r0, sizeof(Bucket)
+ | add FCARG2a, sizeof(Bucket)
| jmp <1
|3:
@@ -14905,10 +14874,8 @@ static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t o
zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
uint32_t val_info;
- | mov FCARG2a, r0
- |4:
| // Z_FE_POS_P(array) = pos + 1;
- | mov dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)], FCARG1d
+ | mov dword [FP + opline->op1.var + offsetof(zval, u2.fe_pos)], eax
if (RETURN_VALUE_USED(opline)) {
zend_jit_addr res_addr = RES_ADDR();
@@ -14916,19 +14883,19 @@ static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t o
if ((op1_info & MAY_BE_ARRAY_KEY_LONG)
&& (op1_info & MAY_BE_ARRAY_KEY_STRING)) {
| // if (!p->key) {
- | cmp aword [r0 + offsetof(Bucket, key)], 0
+ | cmp aword [FCARG2a + offsetof(Bucket, key)], 0
| jz >2
}
if (op1_info & MAY_BE_ARRAY_KEY_STRING) {
| // ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key);
- | mov FCARG1a, aword [r0 + offsetof(Bucket, key)]
- | SET_ZVAL_PTR res_addr, FCARG1a
- | test dword [FCARG1a + offsetof(zend_refcounted, gc.u.type_info)], IS_STR_INTERNED
+ | mov r0, aword [FCARG2a + offsetof(Bucket, key)]
+ | SET_ZVAL_PTR res_addr, r0
+ | test dword [r0 + offsetof(zend_refcounted, gc.u.type_info)], IS_STR_INTERNED
| jz >1
| SET_ZVAL_TYPE_INFO res_addr, IS_STRING
| jmp >3
|1:
- | GC_ADDREF FCARG1a
+ | GC_ADDREF r0
| SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX
if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
@@ -14938,8 +14905,8 @@ static int zend_jit_fe_fetch(dasm_State **Dst, const zend_op *opline, uint32_t o
}
if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
| // ZVAL_LONG(EX_VAR(opline->result.var), p->h);
- | mov FCARG1a, aword [r0 + offsetof(Bucket, h)]
- | SET_ZVAL_LVAL res_addr, FCARG1a
+ | mov r0, aword [FCARG2a + offsetof(Bucket, h)]
+ | SET_ZVAL_LVAL res_addr, r0
| SET_ZVAL_TYPE_INFO res_addr, IS_LONG
}
|3:
@@ -15094,7 +15061,7 @@ static int zend_jit_in_array(dasm_State **Dst, const zend_op *opline, uint32_t o
return 1;
}
-static zend_bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_addr)
+static bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_addr)
{
int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
@@ -15107,7 +15074,7 @@ static zend_bool zend_jit_noref_guard(dasm_State **Dst, const zend_op *opline, z
return 1;
}
-static zend_bool zend_jit_fetch_reference(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr, zend_bool add_ref_guard, zend_bool add_type_guard)
+static bool zend_jit_fetch_reference(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr, bool add_ref_guard, bool add_type_guard)
{
zend_jit_addr var_addr = *var_addr_ptr;
uint32_t var_info = *var_info_ptr;
@@ -15163,7 +15130,7 @@ static zend_bool zend_jit_fetch_reference(dasm_State **Dst, const zend_op *oplin
return 1;
}
-static zend_bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr, zend_bool add_indirect_guard)
+static bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr, bool add_indirect_guard)
{
zend_jit_addr var_addr = *var_addr_ptr;
uint32_t var_info = *var_info_ptr;
@@ -15226,7 +15193,7 @@ static zend_bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *op
return 1;
}
-static zend_bool zend_jit_may_reuse_reg(const zend_op *opline, const zend_ssa_op *ssa_op, zend_ssa *ssa, int def_var, int use_var)
+static bool zend_jit_may_reuse_reg(const zend_op *opline, const zend_ssa_op *ssa_op, zend_ssa *ssa, int def_var, int use_var)
{
if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) {
return 0;
@@ -15258,7 +15225,7 @@ static zend_bool zend_jit_may_reuse_reg(const zend_op *opline, const zend_ssa_op
return 0;
}
-static zend_bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, zend_jit_trace_rec *trace)
+static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, zend_jit_trace_rec *trace)
{
uint32_t op1_info, op2_info;
@@ -15329,7 +15296,7 @@ static zend_bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zen
return 0;
}
-static zend_bool zend_jit_var_supports_reg(zend_ssa *ssa, int var)
+static bool zend_jit_var_supports_reg(zend_ssa *ssa, int var)
{
if (ssa->vars[var].no_val) {
/* we don't need the value */
@@ -15363,7 +15330,7 @@ static zend_bool zend_jit_var_supports_reg(zend_ssa *ssa, int var)
return 1;
}
-static zend_bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, int var)
+static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, int var)
{
if (!zend_jit_var_supports_reg(ssa, var)) {
return 0;
@@ -15391,7 +15358,7 @@ static zend_bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa
return 1;
}
-static zend_bool zend_needs_extra_reg_for_const(const zend_op *opline, zend_uchar op_type, znode_op op)
+static bool zend_needs_extra_reg_for_const(const zend_op *opline, zend_uchar op_type, znode_op op)
{
|.if X64
|| if (op_type == IS_CONST) {
@@ -15406,7 +15373,7 @@ static zend_bool zend_needs_extra_reg_for_const(const zend_op *opline, zend_ucha
return 0;
}
-static zend_regset zend_jit_get_def_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, zend_bool last_use)
+static zend_regset zend_jit_get_def_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, bool last_use)
{
uint32_t op1_info, op2_info;
@@ -15428,7 +15395,7 @@ static zend_regset zend_jit_get_def_scratch_regset(const zend_op *opline, const
return ZEND_REGSET_EMPTY;
}
-static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, zend_bool last_use)
+static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, bool last_use)
{
uint32_t op1_info, op2_info, res_info;
zend_regset regset = ZEND_REGSET_SCRATCH;
diff --git a/ext/opcache/jit/zend_jit_x86.h b/ext/opcache/jit/zend_jit_x86.h
index 10a82db14f..e9476ebb31 100644
--- a/ext/opcache/jit/zend_jit_x86.h
+++ b/ext/opcache/jit/zend_jit_x86.h
@@ -318,7 +318,7 @@ static zend_always_inline zend_jit_addr _zend_jit_decode_op(zend_uchar op_type,
#define OP1_DATA_DEF_REG_ADDR() \
OP_REG_ADDR(opline + 1, op1_type, op1, op1_def)
-static zend_always_inline zend_bool zend_jit_same_addr(zend_jit_addr addr1, zend_jit_addr addr2)
+static zend_always_inline bool zend_jit_same_addr(zend_jit_addr addr1, zend_jit_addr addr2)
{
if (addr1 == addr2) {
return 1;
diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c
index 3d2884e948..6627633694 100644
--- a/ext/opcache/shared_alloc_mmap.c
+++ b/ext/opcache/shared_alloc_mmap.c
@@ -29,6 +29,10 @@
#include <stdlib.h>
#include <sys/mman.h>
+#ifdef __APPLE__
+#include <mach/vm_statistics.h>
+#endif
+
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
# define MAP_ANONYMOUS MAP_ANON
#endif
@@ -39,10 +43,14 @@
static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
{
zend_shared_segment *shared_segment;
- int flags = PROT_READ | PROT_WRITE;
+ int flags = PROT_READ | PROT_WRITE, fd = -1;
void *p;
#ifdef PROT_MPROTECT
- flags |= PROT_MPROTECT(PROT_EXEC);
+ flags |= PROT_MPROTECT(PROT_EXEC);
+#endif
+#ifdef VM_MAKE_TAG
+ /* allows tracking segments via tools such as vmmap */
+ fd = VM_MAKE_TAG(251U);
#endif
#ifdef MAP_HUGETLB
size_t huge_page_size = 2 * 1024 * 1024;
@@ -62,7 +70,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
/* to got HUGE PAGES in low 32-bit address we have to reserve address
space and then remap it using MAP_HUGETLB */
- p = mmap(NULL, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
+ p = mmap(NULL, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, fd, 0);
if (p != MAP_FAILED) {
munmap(p, requested_size);
p = (void*)(ZEND_MM_ALIGNED_SIZE_EX((ptrdiff_t)p, huge_page_size));
@@ -70,26 +78,26 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
if (p != MAP_FAILED) {
goto success;
} else {
- p = mmap(NULL, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
+ p = mmap(NULL, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, fd, 0);
if (p != MAP_FAILED) {
goto success;
}
}
}
# endif
- p = mmap(0, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_HUGETLB, -1, 0);
+ p = mmap(0, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_HUGETLB, fd, 0);
if (p != MAP_FAILED) {
goto success;
}
}
#elif defined(PREFER_MAP_32BIT) && defined(__x86_64__) && defined(MAP_32BIT)
- p = mmap(NULL, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
+ p = mmap(NULL, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, fd, 0);
if (p != MAP_FAILED) {
goto success;
}
#endif
- p = mmap(0, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+ p = mmap(0, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS, fd, 0);
if (p == MAP_FAILED) {
*error_in = "mmap";
return ALLOC_FAILURE;
diff --git a/ext/opcache/shared_alloc_win32.c b/ext/opcache/shared_alloc_win32.c
index 06df5270d4..780fd78f89 100644
--- a/ext/opcache/shared_alloc_win32.c
+++ b/ext/opcache/shared_alloc_win32.c
@@ -142,7 +142,7 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
}
pre_size = ZEND_ALIGNED_SIZE(sizeof(zend_smm_shared_globals)) + ZEND_ALIGNED_SIZE(sizeof(zend_shared_segment)) + ZEND_ALIGNED_SIZE(sizeof(void *)) + ZEND_ALIGNED_SIZE(sizeof(int));
- /* Map only part of SHM to have access opcache shared globals */
+ /* Map only part of SHM to have access to opcache shared globals */
mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, pre_size + ZEND_ALIGNED_SIZE(sizeof(zend_accel_shared_globals)), NULL);
if (mapping_base == NULL) {
err = GetLastError();
@@ -210,7 +210,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
zend_shared_alloc_lock_win32();
/* Mapping retries: When Apache2 restarts, the parent process startup routine
- can be called before the child process is killed. In this case, the map will fail
+ can be called before the child process is killed. In this case, the mapping will fail
and we have to sleep some time (until the child releases the mapping object) and retry.*/
do {
memfile = OpenFileMapping(FILE_MAP_READ|FILE_MAP_WRITE|FILE_MAP_EXECUTE, 0, create_name_with_username(ACCEL_FILEMAP_NAME));
@@ -267,15 +267,15 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_
return ALLOC_FAILURE;
}
- /* Starting from windows Vista, heap randomization occurs which might cause our mapping base to
- be taken (fail to map). So under Vista, we try to map into a hard coded predefined addresses
+ /* Starting from Windows Vista, heap randomization occurs which might cause our mapping base to
+ be taken (fail to map). So we try to map into one of the hard coded predefined addresses
in high memory. */
if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) {
wanted_mapping_base = vista_mapping_base_set;
} else {
char *s = ZCG(accel_directives).mmap_base;
- /* skip leading 0x, %p assumes hexdeciaml format anyway */
+ /* skip leading 0x, %p assumes hexdecimal format anyway */
if (*s == '0' && *(s + 1) == 'x') {
s += 2;
}
diff --git a/ext/opcache/tests/bug65915.phpt b/ext/opcache/tests/bug65915.phpt
index c616c4fb5f..b62dbd8ea7 100644
--- a/ext/opcache/tests/bug65915.phpt
+++ b/ext/opcache/tests/bug65915.phpt
@@ -5,7 +5,11 @@ opcache.enable=1
opcache.enable_cli=1
opcache.file_cache_only=0
--SKIPIF--
-<?php require_once('skipif.inc'); ?>
+<?php
+require_once('skipif.inc');
+// We don't invalidate the file after the second write.
+if (getenv('SKIP_REPEAT')) die("skip Not repeatable");
+?>
--FILE--
<?php
$tmp = __DIR__ . "/bug65915.inc.php";
diff --git a/ext/opcache/tests/bug76337.phpt b/ext/opcache/tests/bug76337.phpt
index 384bb074ed..a49f2788b7 100644
--- a/ext/opcache/tests/bug76337.phpt
+++ b/ext/opcache/tests/bug76337.phpt
@@ -2,7 +2,7 @@
Bug 76337: segmentation fault when an extension use zend_register_class_alias() and opcache enabled
--SKIPIF--
<?php require_once('skipif.inc'); ?>
-<?php if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded');
+<?php if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded');
--INI--
opcache.enable=1
opcache.enable_cli=1
diff --git a/ext/opcache/tests/bug78961.phpt b/ext/opcache/tests/bug78961.phpt
deleted file mode 100644
index 736a747f43..0000000000
--- a/ext/opcache/tests/bug78961.phpt
+++ /dev/null
@@ -1,17 +0,0 @@
---TEST--
-Bug #78961 (erroneous optimization of re-assigned $GLOBALS)
---INI--
-opcache.enable=1
-opcache.enable_cli=1
-opcache.optimization_level=-1
---SKIPIF--
-<?php require_once('skipif.inc'); ?>
---FILE--
-<?php
-$GLOBALS = array();
-$GLOBALS['td'] = array();
-$GLOBALS['td']['nsno'] = 3;
-var_dump($GLOBALS['td']['nsno']);
-?>
---EXPECT--
-int(3)
diff --git a/ext/opcache/tests/internal_func_info_static_method.phpt b/ext/opcache/tests/internal_func_info_static_method.phpt
index df4e9b2aab..1a189b00a8 100644
--- a/ext/opcache/tests/internal_func_info_static_method.phpt
+++ b/ext/opcache/tests/internal_func_info_static_method.phpt
@@ -2,7 +2,7 @@
Internal static methods should not be confused with global functions
--SKIPIF--
<?php
-if (!extension_loaded('zend-test')) die('skip requires zend-test');
+if (!extension_loaded('zend_test')) die('skip requires zend_test');
?>
--FILE--
<?php
diff --git a/ext/opcache/tests/jit/bug80426.phpt b/ext/opcache/tests/jit/bug80426.phpt
index 19364e8a6c..c961bed472 100644
--- a/ext/opcache/tests/jit/bug80426.phpt
+++ b/ext/opcache/tests/jit/bug80426.phpt
@@ -8,7 +8,7 @@ zend_test.replace_zend_execute_ex=1
--SKIPIF--
<?php require_once('skipif.inc'); ?>
<?php if (!isset(opcache_get_status()["jit"])) die('skip: JIT is not available'); ?>
-<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?>
+<?php if (!extension_loaded('zend_test')) die('skip: zend_test extension required'); ?>
--FILE--
<?php
diff --git a/ext/opcache/tests/opt/dce_009.phpt b/ext/opcache/tests/opt/dce_009.phpt
new file mode 100644
index 0000000000..0503614f34
--- /dev/null
+++ b/ext/opcache/tests/opt/dce_009.phpt
@@ -0,0 +1,71 @@
+--TEST--
+DCE 009: Foreach over empty array is a no-op
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.opt_debug_level=0x20000
+opcache.preload=
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+class Loop {
+ const VALUES = [];
+ public static function test() {
+ echo "Start\n";
+ $y = [];
+ foreach ($y as $x) {
+ }
+ echo "Done\n";
+ }
+ public static function test2() {
+ foreach (self::VALUES as $x) {
+ }
+ }
+ public static function test3() {
+ foreach ([] as $k => &$v) {
+ }
+ }
+}
+Loop::test();
+Loop::test2();
+Loop::test3();
+--EXPECTF--
+$_main:
+ ; (lines=7, args=0, vars=0, tmps=0)
+ ; (after optimizer)
+ ; %sdce_009.php:1-23
+0000 INIT_STATIC_METHOD_CALL 0 string("Loop") string("test")
+0001 DO_UCALL
+0002 INIT_STATIC_METHOD_CALL 0 string("Loop") string("test2")
+0003 DO_UCALL
+0004 INIT_STATIC_METHOD_CALL 0 string("Loop") string("test3")
+0005 DO_UCALL
+0006 RETURN int(1)
+
+Loop::test:
+ ; (lines=3, args=0, vars=0, tmps=0)
+ ; (after optimizer)
+ ; %sdce_009.php:4-10
+0000 ECHO string("Start
+")
+0001 ECHO string("Done
+")
+0002 RETURN null
+
+Loop::test2:
+ ; (lines=1, args=0, vars=0, tmps=0)
+ ; (after optimizer)
+ ; %sdce_009.php:11-14
+0000 RETURN null
+
+Loop::test3:
+ ; (lines=3, args=0, vars=0, tmps=1)
+ ; (after optimizer)
+ ; %sdce_009.php:15-18
+0000 V0 = FE_RESET_RW array(...) 0001
+0001 FE_FREE V0
+0002 RETURN null
+Start
+Done
diff --git a/ext/opcache/tests/zzz_basic_logging.phpt b/ext/opcache/tests/zzz_basic_logging.phpt
index bf04b50861..0fbf308a51 100644
--- a/ext/opcache/tests/zzz_basic_logging.phpt
+++ b/ext/opcache/tests/zzz_basic_logging.phpt
@@ -12,7 +12,11 @@ opcache.log_verbosity_level=4
opcache.huge_code_pages=0
opcache.preload=
--SKIPIF--
-<?php require_once('skipif.inc'); ?>
+<?php
+require_once('skipif.inc');
+// Prints "Debug Restarting!" message on next request.
+if (getenv('SKIP_REPEAT')) die("skip Not repeatable");
+?>
--FILE--
<?php
echo "Foo Bar\n";
diff --git a/ext/opcache/zend_accelerator_blacklist.c b/ext/opcache/zend_accelerator_blacklist.c
index febe38aa92..df9417db34 100644
--- a/ext/opcache/zend_accelerator_blacklist.c
+++ b/ext/opcache/zend_accelerator_blacklist.c
@@ -60,7 +60,7 @@ void zend_accel_blacklist_init(zend_blacklist *blacklist)
blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size);
if (!blacklist->entries) {
- zend_accel_error(ACCEL_LOG_FATAL, "Blacklist initialization: no memory\n");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Blacklist initialization: no memory\n");
return;
}
blacklist->regexp_list = NULL;
@@ -68,7 +68,7 @@ void zend_accel_blacklist_init(zend_blacklist *blacklist)
static void blacklist_report_regexp_error(const char *pcre_error, int pcre_error_offset)
{
- zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation failed (offset: %d), %s\n", pcre_error_offset, pcre_error);
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Blacklist compilation failed (offset: %d), %s\n", pcre_error_offset, pcre_error);
}
static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
@@ -163,7 +163,7 @@ static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
if (*c || i == blacklist->pos - 1) {
if (*c) {
if (!backtrack) {
- zend_accel_error(ACCEL_LOG_ERROR, "Too long blacklist entry\n");
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Too long blacklist entry\n");
}
p = backtrack;
} else {
@@ -173,7 +173,7 @@ static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
it = (zend_regexp_list*)malloc(sizeof(zend_regexp_list));
if (!it) {
- zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "malloc() failed\n");
return;
}
it->next = NULL;
@@ -304,7 +304,7 @@ static void zend_accel_blacklist_loadone(zend_blacklist *blacklist, char *filena
blacklist->entries[blacklist->pos].path_length = path_length;
blacklist->entries[blacklist->pos].path = (char *)malloc(path_length + 1);
if (!blacklist->entries[blacklist->pos].path) {
- zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "malloc() failed\n");
fclose(fp);
return;
}
@@ -346,7 +346,7 @@ void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
zend_accel_blacklist_update_regexp(blacklist);
}
-zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path, size_t verify_path_len)
+bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path, size_t verify_path_len)
{
int ret = 0;
zend_regexp_list *regexp_list_it = blacklist->regexp_list;
diff --git a/ext/opcache/zend_accelerator_blacklist.h b/ext/opcache/zend_accelerator_blacklist.h
index 16c75bb396..a75a0e1ed8 100644
--- a/ext/opcache/zend_accelerator_blacklist.h
+++ b/ext/opcache/zend_accelerator_blacklist.h
@@ -45,7 +45,7 @@ void zend_accel_blacklist_init(zend_blacklist *blacklist);
void zend_accel_blacklist_shutdown(zend_blacklist *blacklist);
void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename);
-zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path, size_t verify_path_len);
+bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path, size_t verify_path_len);
void zend_accel_blacklist_apply(zend_blacklist *blacklist, blacklist_apply_func_arg_t func, void *argument);
#endif /* ZEND_ACCELERATOR_BLACKLIST_H */
diff --git a/ext/opcache/zend_accelerator_debug.c b/ext/opcache/zend_accelerator_debug.c
index f91c269975..a3db2a7215 100644
--- a/ext/opcache/zend_accelerator_debug.c
+++ b/ext/opcache/zend_accelerator_debug.c
@@ -28,9 +28,8 @@
#endif
#include "ZendAccelerator.h"
-void zend_accel_error(int type, const char *format, ...)
+static void zend_accel_error_va_args(int type, const char *format, va_list args)
{
- va_list args;
time_t timestamp;
char *time_string;
FILE * fLog = NULL;
@@ -77,9 +76,7 @@ void zend_accel_error(int type, const char *format, ...)
break;
}
- va_start(args, format);
vfprintf(fLog, format, args);
- va_end(args);
fprintf(fLog, "\n");
fflush(fLog);
@@ -98,3 +95,22 @@ void zend_accel_error(int type, const char *format, ...)
}
}
+
+void zend_accel_error(int type, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ zend_accel_error_va_args(type, format, args);
+ va_end(args);
+}
+
+ZEND_NORETURN void zend_accel_error_noreturn(int type, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ ZEND_ASSERT(type == ACCEL_LOG_FATAL || type == ACCEL_LOG_ERROR);
+ zend_accel_error_va_args(type, format, args);
+ va_end(args);
+ /* Should never reach this. */
+ abort();
+}
diff --git a/ext/opcache/zend_accelerator_debug.h b/ext/opcache/zend_accelerator_debug.h
index 234d0367de..61456096d1 100644
--- a/ext/opcache/zend_accelerator_debug.h
+++ b/ext/opcache/zend_accelerator_debug.h
@@ -29,5 +29,6 @@
#define ACCEL_LOG_DEBUG 4
void zend_accel_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
+ZEND_NORETURN void zend_accel_error_noreturn(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
#endif /* _ZEND_ACCELERATOR_DEBUG_H */
diff --git a/ext/opcache/zend_accelerator_hash.c b/ext/opcache/zend_accelerator_hash.c
index 7cd9ef1ef4..b3f2dfce20 100644
--- a/ext/opcache/zend_accelerator_hash.c
+++ b/ext/opcache/zend_accelerator_hash.c
@@ -54,14 +54,14 @@ void zend_accel_hash_init(zend_accel_hash *accel_hash, uint32_t hash_size)
/* set up hash pointers table */
accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
if (!accel_hash->hash_table) {
- zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
return;
}
/* set up hash values table */
accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries);
if (!accel_hash->hash_entries) {
- zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
return;
}
memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
@@ -71,7 +71,7 @@ void zend_accel_hash_init(zend_accel_hash *accel_hash, uint32_t hash_size)
* Returns pointer the actual hash entry on success
* key needs to be already allocated as it is not copied
*/
-zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, const char *key, uint32_t key_length, zend_bool indirect, void *data)
+zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, const char *key, uint32_t key_length, bool indirect, void *data)
{
zend_ulong hash_value;
zend_ulong index;
diff --git a/ext/opcache/zend_accelerator_hash.h b/ext/opcache/zend_accelerator_hash.h
index dc18ca54e1..88edbd23d8 100644
--- a/ext/opcache/zend_accelerator_hash.h
+++ b/ext/opcache/zend_accelerator_hash.h
@@ -50,7 +50,7 @@ struct _zend_accel_hash_entry {
zend_accel_hash_entry *next;
void *data;
uint32_t key_length;
- zend_bool indirect;
+ bool indirect;
};
typedef struct _zend_accel_hash {
@@ -68,7 +68,7 @@ zend_accel_hash_entry* zend_accel_hash_update(
zend_accel_hash *accel_hash,
const char *key,
uint32_t key_length,
- zend_bool indirect,
+ bool indirect,
void *data);
void* zend_accel_hash_find(
@@ -94,7 +94,7 @@ int zend_accel_hash_unlink(
const char *key,
uint32_t key_length);
-static inline zend_bool zend_accel_hash_is_full(zend_accel_hash *accel_hash)
+static inline bool zend_accel_hash_is_full(zend_accel_hash *accel_hash)
{
if (accel_hash->num_entries == accel_hash->max_num_entries) {
return 1;
diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c
index d68ab9531a..1abbb5bb64 100644
--- a/ext/opcache/zend_accelerator_module.c
+++ b/ext/opcache/zend_accelerator_module.c
@@ -116,7 +116,7 @@ static ZEND_INI_MH(OnEnable)
return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
} else {
/* It may be only temporary disabled */
- zend_bool *p = (zend_bool *) ZEND_INI_GET_ADDR();
+ bool *p = (bool *) ZEND_INI_GET_ADDR();
if ((ZSTR_LEN(new_value) == 2 && strcasecmp("on", ZSTR_VAL(new_value)) == 0) ||
(ZSTR_LEN(new_value) == 3 && strcasecmp("yes", ZSTR_VAL(new_value)) == 0) ||
(ZSTR_LEN(new_value) == 4 && strcasecmp("true", ZSTR_VAL(new_value)) == 0) ||
@@ -572,7 +572,7 @@ ZEND_FUNCTION(opcache_get_status)
{
zend_long reqs;
zval memory_usage, statistics, scripts;
- zend_bool fetch_scripts = 1;
+ bool fetch_scripts = 1;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &fetch_scripts) == FAILURE) {
RETURN_THROWS();
@@ -839,7 +839,7 @@ ZEND_FUNCTION(opcache_invalidate)
{
char *script_name;
size_t script_name_len;
- zend_bool force = 0;
+ bool force = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &script_name, &script_name_len, &force) == FAILURE) {
RETURN_THROWS();
diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c
index cfd70eec12..d3011da941 100644
--- a/ext/opcache/zend_accelerator_util_funcs.c
+++ b/ext/opcache/zend_accelerator_util_funcs.c
@@ -25,15 +25,6 @@
#include "zend_persist.h"
#include "zend_shared_alloc.h"
-#if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
-/* If sizeof(void*) == sizeof(zend_ulong) we can use zend_hash index functions */
-# define accel_xlat_set(old, new) zend_hash_index_add_new_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old), (new))
-# define accel_xlat_get(old) zend_hash_index_find_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old))
-#else
-# define accel_xlat_set(old, new) zend_hash_str_add_new_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (new))
-# define accel_xlat_get(old) zend_hash_str_find_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*))
-#endif
-
#define IN_ARENA(ptr) \
((void*)(ptr) >= ZCG(current_persistent_script)->arena_mem && \
(void*)(ptr) < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size))
@@ -342,13 +333,7 @@ static void zend_class_copy_ctor(zend_class_entry **pce)
}
if (ce->num_interfaces) {
- zend_class_name *interface_names;
-
- if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
- interface_names = emalloc(sizeof(zend_class_name) * ce->num_interfaces);
- memcpy(interface_names, ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
- ce->interface_names = interface_names;
- } else {
+ if (ce->ce_flags & ZEND_ACC_LINKED) {
zend_class_entry **interfaces = emalloc(sizeof(zend_class_entry*) * ce->num_interfaces);
uint32_t i;
@@ -376,50 +361,6 @@ static void zend_class_copy_ctor(zend_class_entry **pce)
zend_update_inherited_handler(__debugInfo);
zend_update_inherited_handler(__serialize);
zend_update_inherited_handler(__unserialize);
-
-/* 5.4 traits */
- if (ce->num_traits) {
- zend_class_name *trait_names = emalloc(sizeof(zend_class_name) * ce->num_traits);
-
- memcpy(trait_names, ce->trait_names, sizeof(zend_class_name) * ce->num_traits);
- ce->trait_names = trait_names;
-
- if (ce->trait_aliases) {
- zend_trait_alias **trait_aliases;
- int i = 0;
-
- while (ce->trait_aliases[i]) {
- i++;
- }
- trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i + 1));
- i = 0;
- while (ce->trait_aliases[i]) {
- trait_aliases[i] = emalloc(sizeof(zend_trait_alias));
- memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias));
- i++;
- }
- trait_aliases[i] = NULL;
- ce->trait_aliases = trait_aliases;
- }
-
- if (ce->trait_precedences) {
- zend_trait_precedence **trait_precedences;
- int i = 0;
-
- while (ce->trait_precedences[i]) {
- i++;
- }
- trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i + 1));
- i = 0;
- while (ce->trait_precedences[i]) {
- trait_precedences[i] = emalloc(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*));
- memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*));
- i++;
- }
- trait_precedences[i] = NULL;
- ce->trait_precedences = trait_precedences;
- }
- }
}
static void zend_accel_function_hash_copy(HashTable *target, HashTable *source)
@@ -451,50 +392,6 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source)
goto failure;
}
} else {
- _zend_hash_append_ptr(target, p->key, Z_PTR(p->val));
- }
- }
- target->nInternalPointer = 0;
- return;
-
-failure:
- function1 = Z_PTR(p->val);
- function2 = Z_PTR_P(t);
- CG(in_compilation) = 1;
- zend_set_compiled_filename(function1->op_array.filename);
- CG(zend_lineno) = function1->op_array.opcodes[0].lineno;
- if (function2->type == ZEND_USER_FUNCTION
- && function2->op_array.last > 0) {
- zend_error(E_ERROR, "Cannot redeclare %s() (previously declared in %s:%d)",
- ZSTR_VAL(function1->common.function_name),
- ZSTR_VAL(function2->op_array.filename),
- (int)function2->op_array.opcodes[0].lineno);
- } else {
- zend_error(E_ERROR, "Cannot redeclare %s()", ZSTR_VAL(function1->common.function_name));
- }
-}
-
-static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable *source)
-{
- zend_function *function1, *function2;
- Bucket *p, *end;
- zval *t;
-
- zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
- p = source->arData;
- end = p + source->nNumUsed;
- for (; p != end; p++) {
- ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
- ZEND_ASSERT(p->key);
- t = zend_hash_find_ex(target, p->key, 1);
- if (UNEXPECTED(t != NULL)) {
- if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) {
- /* See comment in zend_accel_function_hash_copy(). */
- continue;
- } else {
- goto failure;
- }
- } else {
_zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1);
}
}
@@ -527,7 +424,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source)
p = source->arData;
end = p + source->nNumUsed;
for (; p != end; p++) {
- if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
+ ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
ZEND_ASSERT(p->key);
t = zend_hash_find_ex(target, p->key, 1);
if (UNEXPECTED(t != NULL)) {
@@ -548,7 +445,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source)
continue;
}
} else {
- t = _zend_hash_append_ptr(target, p->key, Z_PTR(p->val));
+ t = _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1);
}
}
target->nInternalPointer = 0;
@@ -753,9 +650,11 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
*op_array = persistent_script->script.main_op_array;
ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
- if (EXPECTED(from_shared_memory)) {
- zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0);
+ if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
+ zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table);
+ }
+ if (EXPECTED(from_shared_memory)) {
ZCG(current_persistent_script) = persistent_script;
ZCG(arena_mem) = NULL;
if (EXPECTED(persistent_script->arena_size)) {
@@ -775,11 +674,6 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
zend_accel_class_hash_copy_from_shm(CG(class_table), &persistent_script->script.class_table);
}
- /* we must first to copy all classes and then prepare functions, since functions may try to bind
- classes - which depend on pre-bind class entries existent in the class table */
- if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
- zend_accel_function_hash_copy_from_shm(CG(function_table), &persistent_script->script.function_table);
- }
/* Register __COMPILER_HALT_OFFSET__ constant */
if (persistent_script->compiler_halt_offset != 0 &&
@@ -794,13 +688,9 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
zend_string_release_ex(name, 0);
}
- zend_hash_destroy(&ZCG(bind_hash));
ZCG(current_persistent_script) = NULL;
zend_map_ptr_extend(ZCSG(map_ptr_last));
} else /* if (!from_shared_memory) */ {
- if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
- zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table);
- }
if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table);
}
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index efadcb91fb..9bdba91639 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -285,11 +285,11 @@ static HashTable *zend_persist_attributes(HashTable *attributes)
return ptr;
}
-static void zend_persist_type(zend_type *type) {
+static void zend_persist_type(zend_type *type, bool use_arena) {
if (ZEND_TYPE_HAS_LIST(*type)) {
zend_type_list *list = ZEND_TYPE_LIST(*type);
if (ZEND_TYPE_USES_ARENA(*type)) {
- if (!ZCG(is_immutable_class)) {
+ if (!ZCG(is_immutable_class) && use_arena) {
list = zend_shared_memdup_arena_put(list, ZEND_TYPE_LIST_SIZE(list->num_types));
} else {
/* Moved from arena to SHM because type list was fully resolved. */
@@ -575,7 +575,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
if (arg_info[i].name) {
zend_accel_store_interned_string(arg_info[i].name);
}
- zend_persist_type(&arg_info[i].type);
+ zend_persist_type(&arg_info[i].type, 0);
}
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
arg_info++;
@@ -738,7 +738,7 @@ static zend_property_info *zend_persist_property_info(zend_property_info *prop)
if (prop->attributes) {
prop->attributes = zend_persist_attributes(prop->attributes);
}
- zend_persist_type(&prop->type);
+ zend_persist_type(&prop->type, 1);
return prop;
}
@@ -806,6 +806,7 @@ static void zend_persist_class_entry(zval *zv)
ZCG(is_immutable_class) = 0;
ce = Z_PTR_P(zv) = zend_shared_memdup_arena_put(ce, sizeof(zend_class_entry));
}
+ ce->ce_flags |= ZEND_ACC_CACHED;
zend_accel_store_interned_string(ce->name);
if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) {
zend_accel_store_interned_string(ce->parent_name);
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index 9de02e91e2..7731b9a970 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -166,10 +166,10 @@ static void zend_persist_attributes_calc(HashTable *attributes)
}
}
-static void zend_persist_type_calc(zend_type *type)
+static void zend_persist_type_calc(zend_type *type, bool use_arena)
{
if (ZEND_TYPE_HAS_LIST(*type)) {
- if (ZEND_TYPE_USES_ARENA(*type) && !ZCG(is_immutable_class)) {
+ if (ZEND_TYPE_USES_ARENA(*type) && !ZCG(is_immutable_class) && use_arena) {
ADD_ARENA_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
} else {
ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
@@ -254,7 +254,7 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
if (arg_info[i].name) {
ADD_INTERNED_STRING(arg_info[i].name);
}
- zend_persist_type_calc(&arg_info[i].type);
+ zend_persist_type_calc(&arg_info[i].type, 0);
}
}
@@ -338,7 +338,7 @@ static void zend_persist_property_info_calc(zend_property_info *prop)
{
ADD_SIZE_EX(sizeof(zend_property_info));
ADD_INTERNED_STRING(prop->name);
- zend_persist_type_calc(&prop->type);
+ zend_persist_type_calc(&prop->type, 1);
if (ZCG(accel_directives).save_comments && prop->doc_comment) {
ADD_STRING(prop->doc_comment);
}
diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c
index d32a70b7e8..698c2884ca 100644
--- a/ext/opcache/zend_shared_alloc.c
+++ b/ext/opcache/zend_shared_alloc.c
@@ -86,7 +86,7 @@ void zend_shared_alloc_create_lock(char *lockfile_path)
fchmod(lock_file, 0666);
if (lock_file == -1) {
- zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
}
val = fcntl(lock_file, F_GETFD, 0);
val |= FD_CLOEXEC;
@@ -98,7 +98,7 @@ void zend_shared_alloc_create_lock(char *lockfile_path)
static void no_memory_bailout(size_t allocate_size, char *error)
{
- zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %zu bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %zu bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
}
static void copy_shared_segments(void *to, void *from, int count, int size)
@@ -231,14 +231,14 @@ int zend_shared_alloc_startup(size_t requested_size, size_t reserved_size)
p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
if (!p_tmp_shared_globals) {
- zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
return ALLOC_FAILURE;
}
memset(p_tmp_shared_globals, 0, sizeof(zend_smm_shared_globals));
tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *));
if (!tmp_shared_segments) {
- zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
return ALLOC_FAILURE;
}
@@ -252,7 +252,7 @@ int zend_shared_alloc_startup(size_t requested_size, size_t reserved_size)
ZSMMG(shared_memory_state).positions = (int *)zend_shared_alloc(sizeof(int) * ZSMMG(shared_segments_count));
if (!ZSMMG(shared_memory_state).positions) {
- zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
return ALLOC_FAILURE;
}
@@ -263,7 +263,7 @@ int zend_shared_alloc_startup(size_t requested_size, size_t reserved_size)
ZSMMG(reserved) = (char*)ZSMMG(shared_segments)[i]->p + ZSMMG(shared_segments)[i]->end;
ZSMMG(reserved_size) = reserved_size;
} else {
- zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
+ zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
return ALLOC_FAILURE;
}
}
@@ -340,7 +340,7 @@ void *zend_shared_alloc(size_t size)
#if 1
if (!ZCG(locked)) {
- zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
}
#endif
if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
@@ -375,7 +375,7 @@ int zend_shared_memdup_size(void *source, size_t size)
return ZEND_ALIGNED_SIZE(size);
}
-static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, zend_bool arena, zend_bool get_xlat, zend_bool set_xlat, zend_bool free_source)
+static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, bool arena, bool get_xlat, bool set_xlat, bool free_source)
{
void *old_p, *retval;
zend_ulong key;
@@ -482,7 +482,7 @@ void zend_shared_alloc_lock(void)
if (errno == EINTR) {
continue;
}
- zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
}
break;
}
@@ -508,7 +508,7 @@ void zend_shared_alloc_unlock(void)
#ifndef ZEND_WIN32
if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
- zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
}
#ifdef ZTS
tsrm_mutex_unlock(zts_lock);
@@ -631,7 +631,7 @@ void zend_accel_shared_protect(int mode)
for (i = 0; i < ZSMMG(shared_segments_count); i++) {
DWORD oldProtect;
if (!VirtualProtect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->end, mode, &oldProtect)) {
- zend_accel_error(ACCEL_LOG_ERROR, "Failed to protect memory");
+ zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Failed to protect memory");
}
}
#endif
diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h
index 1dbc88d42e..8d37376570 100644
--- a/ext/opcache/zend_shared_alloc.h
+++ b/ext/opcache/zend_shared_alloc.h
@@ -109,7 +109,7 @@ typedef struct _zend_smm_shared_globals {
/* Amount of shared memory allocated by garbage */
size_t wasted_shared_memory;
/* No more shared memory flag */
- zend_bool memory_exhausted;
+ bool memory_exhausted;
/* Saved Shared Allocator State */
zend_shared_memory_state shared_memory_state;
/* Pointer to the application's shared data structures */