@@ -332,6 +332,8 @@ void zend_init_compiler_data_structures(void) /* {{{ */
CG(start_lineno) = 0;

CG(encoding_declared) = 0;
CG(memoized_exprs) = NULL;
CG(memoize_mode) = 0;
}
/* }}} */

@@ -1900,6 +1902,7 @@ static inline void zend_update_jump_target(uint32_t opnum_jump, uint32_t opnum_t
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
opline->op2.opline_num = opnum_target;
break;
EMPTY_SWITCH_DEFAULT_CASE()
@@ -1956,6 +1959,43 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
}
/* }}} */

#define ZEND_MEMOIZE_NONE 0
#define ZEND_MEMOIZE_COMPILE 1
#define ZEND_MEMOIZE_FETCH 2

static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
{
int memoize_mode = CG(memoize_mode);
if (memoize_mode == ZEND_MEMOIZE_COMPILE) {
znode memoized_result;

/* Go through normal compilation */
CG(memoize_mode) = ZEND_MEMOIZE_NONE;
zend_compile_expr(result, expr);
CG(memoize_mode) = ZEND_MEMOIZE_COMPILE;

if (result->op_type == IS_VAR) {
zend_emit_op(&memoized_result, ZEND_COPY_TMP, result, NULL);
} else if (result->op_type == IS_TMP_VAR) {
zend_emit_op_tmp(&memoized_result, ZEND_COPY_TMP, result, NULL);
} else {
memoized_result = *result;
}

zend_hash_index_update_mem(
CG(memoized_exprs), (uintptr_t) expr, &memoized_result, sizeof(znode));
} else if (memoize_mode == ZEND_MEMOIZE_FETCH) {
znode *memoized_result = zend_hash_index_find_ptr(CG(memoized_exprs), (uintptr_t) expr);
*result = *memoized_result;
if (result->op_type == IS_CONST) {
Z_TRY_ADDREF(result->u.constant);
}
} else {
ZEND_ASSERT(0);
}
}
/* }}} */

static void zend_emit_return_type_check(
znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */
{
@@ -7220,6 +7260,100 @@ void zend_compile_coalesce(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */

static void znode_dtor(zval *zv) {
efree(Z_PTR_P(zv));
}

void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
zend_ast *default_ast = ast->child[1];

znode var_node_is, var_node_w, default_node, assign_node, *node;
zend_op *opline;
uint32_t coalesce_opnum;
zend_bool need_frees = 0;

/* Remember expressions compiled during the initial BP_VAR_IS lookup,
* to avoid double-evaluation when we compile again with BP_VAR_W. */
HashTable *orig_memoized_exprs = CG(memoized_exprs);
int orig_memoize_mode = CG(memoize_mode);

zend_ensure_writable_variable(var_ast);
if (is_this_fetch(var_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
}

ALLOC_HASHTABLE(CG(memoized_exprs));
zend_hash_init(CG(memoized_exprs), 0, NULL, znode_dtor, 0);

CG(memoize_mode) = ZEND_MEMOIZE_COMPILE;
zend_compile_var(&var_node_is, var_ast, BP_VAR_IS, 0);

coalesce_opnum = get_next_op_number();
zend_emit_op_tmp(result, ZEND_COALESCE, &var_node_is, NULL);

CG(memoize_mode) = ZEND_MEMOIZE_NONE;
zend_compile_expr(&default_node, default_ast);

CG(memoize_mode) = ZEND_MEMOIZE_FETCH;
zend_compile_var(&var_node_w, var_ast, BP_VAR_W, 0);

/* Reproduce some of the zend_compile_assign() opcode fixup logic here. */
opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
switch (var_ast->kind) {
case ZEND_AST_VAR:
zend_emit_op(&assign_node, ZEND_ASSIGN, &var_node_w, &default_node);
break;
case ZEND_AST_STATIC_PROP:
opline->opcode = ZEND_ASSIGN_STATIC_PROP;
zend_emit_op_data(&default_node);
assign_node = var_node_w;
break;
case ZEND_AST_DIM:
opline->opcode = ZEND_ASSIGN_DIM;
zend_emit_op_data(&default_node);
assign_node = var_node_w;
break;
case ZEND_AST_PROP:
opline->opcode = ZEND_ASSIGN_OBJ;
zend_emit_op_data(&default_node);
assign_node = var_node_w;
break;
EMPTY_SWITCH_DEFAULT_CASE();
}

opline = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &assign_node, NULL);
SET_NODE(opline->result, result);

ZEND_HASH_FOREACH_PTR(CG(memoized_exprs), node) {
if (node->op_type == IS_TMP_VAR || node->op_type == IS_VAR) {
need_frees = 1;
break;
}
} ZEND_HASH_FOREACH_END();

/* Free DUPed expressions if there are any */
if (need_frees) {
uint32_t jump_opnum = zend_emit_jump(0);
zend_update_jump_target_to_next(coalesce_opnum);
ZEND_HASH_FOREACH_PTR(CG(memoized_exprs), node) {
if (node->op_type == IS_TMP_VAR || node->op_type == IS_VAR) {
zend_emit_op(NULL, ZEND_FREE, node, NULL);
}
} ZEND_HASH_FOREACH_END();
zend_update_jump_target_to_next(jump_opnum);
} else {
zend_update_jump_target_to_next(coalesce_opnum);
}

zend_hash_destroy(CG(memoized_exprs));
FREE_HASHTABLE(CG(memoized_exprs));
CG(memoized_exprs) = orig_memoized_exprs;
CG(memoize_mode) = orig_memoize_mode;
}
/* }}} */

void zend_compile_print(znode *result, zend_ast *ast) /* {{{ */
{
zend_op *opline;
@@ -8105,6 +8239,11 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
/* CG(zend_lineno) = ast->lineno; */
CG(zend_lineno) = zend_ast_get_lineno(ast);

if (CG(memoize_mode) != ZEND_MEMOIZE_NONE) {
zend_compile_memoized_expr(result, ast);
return;
}

switch (ast->kind) {
case ZEND_AST_ZVAL:
ZVAL_COPY(&result->u.constant, zend_ast_get_zval(ast));
@@ -8172,6 +8311,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_COALESCE:
zend_compile_coalesce(result, ast);
return;
case ZEND_AST_ASSIGN_COALESCE:
zend_compile_assign_coalesce(result, ast);
return;
case ZEND_AST_PRINT:
zend_compile_print(result, ast);
return;
@@ -116,6 +116,8 @@ struct _zend_compiler_globals {
zend_arena *ast_arena;

zend_stack delayed_oplines_stack;
HashTable *memoized_exprs;
int memoize_mode;

void *map_ptr_base;
size_t map_ptr_size;
@@ -60,7 +60,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%right T_YIELD
%right T_DOUBLE_ARROW
%right T_YIELD_FROM
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL T_COALESCE_EQUAL
%left '?' ':'
%right T_COALESCE
%left T_BOOLEAN_OR
@@ -118,6 +118,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token T_XOR_EQUAL "^= (T_XOR_EQUAL)"
%token T_SL_EQUAL "<<= (T_SL_EQUAL)"
%token T_SR_EQUAL ">>= (T_SR_EQUAL)"
%token T_COALESCE_EQUAL "??= (T_COALESCE_EQUAL)"
%token T_BOOLEAN_OR "|| (T_BOOLEAN_OR)"
%token T_BOOLEAN_AND "&& (T_BOOLEAN_AND)"
%token T_IS_EQUAL "== (T_IS_EQUAL)"
@@ -906,6 +907,8 @@ expr:
{ $$ = zend_ast_create_assign_op(ZEND_ASSIGN_SL, $1, $3); }
| variable T_SR_EQUAL expr
{ $$ = zend_ast_create_assign_op(ZEND_ASSIGN_SR, $1, $3); }
| variable T_COALESCE_EQUAL expr
{ $$ = zend_ast_create(ZEND_AST_ASSIGN_COALESCE, $1, $3); }
| variable T_INC { $$ = zend_ast_create(ZEND_AST_POST_INC, $1); }
| T_INC variable { $$ = zend_ast_create(ZEND_AST_PRE_INC, $2); }
| variable T_DEC { $$ = zend_ast_create(ZEND_AST_POST_DEC, $1); }
@@ -1681,6 +1681,10 @@ NEWLINE ("\r"|"\n"|"\r\n")
RETURN_TOKEN(T_XOR_EQUAL);
}

<ST_IN_SCRIPTING>"??=" {
RETURN_TOKEN(T_COALESCE_EQUAL);
}

<ST_IN_SCRIPTING>"||" {
RETURN_TOKEN(T_BOOLEAN_OR);
}
@@ -547,12 +547,27 @@ static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const ze
return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont;
}

static void emit_live_range_raw(
zend_op_array *op_array, uint32_t var_num, uint32_t kind, uint32_t start, uint32_t end) {
zend_live_range *range;

op_array->last_live_range++;
op_array->live_range = erealloc(op_array->live_range,
sizeof(zend_live_range) * op_array->last_live_range);

ZEND_ASSERT(start < end);
range = &op_array->live_range[op_array->last_live_range - 1];
range->var = (uint32_t) (intptr_t) ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var_num);
range->var |= kind;
range->start = start;
range->end = end;
}

static void emit_live_range(
zend_op_array *op_array, uint32_t var_num, uint32_t start, uint32_t end,
zend_needs_live_range_cb needs_live_range) {
zend_op *def_opline = &op_array->opcodes[start], *orig_def_opline = def_opline;
zend_op *use_opline = &op_array->opcodes[end];
zend_live_range *range;
uint32_t kind;

switch (def_opline->opcode) {
@@ -589,7 +604,8 @@ static void emit_live_range(
break;
/* Objects created via ZEND_NEW are only fully initialized
* after the DO_FCALL (constructor call). */
case ZEND_NEW: {
case ZEND_NEW:
{
int level = 0;
while (def_opline + 1 < use_opline) {
def_opline++;
@@ -629,23 +645,49 @@ static void emit_live_range(
kind = ZEND_LIVE_TMPVAR;
break;
}
case ZEND_COPY_TMP:
{
/* COPY_TMP has a split live-range: One from the definition until the use in
* "null" branch, and another from the start of the "non-null" branch to the
* FREE opcode. */
uint32_t rt_var_num =
(uint32_t) (intptr_t) ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var_num);
zend_op *block_start_op = use_opline;

if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
return;
}

while ((block_start_op-1)->opcode == ZEND_FREE) {
block_start_op--;
}

kind = ZEND_LIVE_TMPVAR;
start = block_start_op - op_array->opcodes;
if (start != end) {
emit_live_range_raw(op_array, var_num, kind, start, end);
}

do {
use_opline--;
} while (!(
((use_opline->op1_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op1.var == rt_var_num) ||
((use_opline->op2_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op2.var == rt_var_num)
));

start = def_opline + 1 - op_array->opcodes;
end = use_opline - op_array->opcodes;
emit_live_range_raw(op_array, var_num, kind, start, end);
return;
}
}

/* Check hook to determine whether a live range is necessary, e.g. based on type info. */
if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
return;
}

op_array->last_live_range++;
op_array->live_range = erealloc(op_array->live_range,
sizeof(zend_live_range) * op_array->last_live_range);

ZEND_ASSERT(start < end);
range = &op_array->live_range[op_array->last_live_range - 1];
range->var = (uint32_t) (intptr_t) ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var_num);
range->var |= kind;
range->start = start;
range->end = end;
emit_live_range_raw(op_array, var_num, kind, start, end);
}

static zend_bool is_fake_def(zend_op *opline) {
@@ -659,7 +701,8 @@ static zend_bool keeps_op1_alive(zend_op *opline) {
* it is later freed by something else. */
if (opline->opcode == ZEND_CASE
|| opline->opcode == ZEND_SWITCH_LONG
|| opline->opcode == ZEND_FETCH_LIST_R) {
|| opline->opcode == ZEND_FETCH_LIST_R
|| opline->opcode == ZEND_COPY_TMP) {
return 1;
}
ZEND_ASSERT(opline->opcode != ZEND_SWITCH_STRING
@@ -8449,6 +8449,16 @@ ZEND_VM_HANDLER(195, ZEND_FUNC_GET_ARGS, UNUSED|CONST, UNUSED)
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HANDLER(207, ZEND_COPY_TMP, TMPVAR, UNUSED)
{
USE_OPLINE
zend_free_op free_op1;
zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R);
zval *result = EX_VAR(opline->result.var);
ZVAL_COPY(result, value);
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_JMP, (OP_JMP_ADDR(op, op->op1) > op), ZEND_JMP_FORWARD, JMP_ADDR, ANY)
{
USE_OPLINE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COPY_TMP_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *value = _get_zval_ptr_var(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
zval *result = EX_VAR(opline->result.var);
ZVAL_COPY(result, value);
ZEND_VM_NEXT_OPCODE();
}

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
(void*)&&ZEND_POST_DEC_STATIC_PROP_SPEC_CV_VAR_LABEL,
(void*)&&ZEND_POST_DEC_STATIC_PROP_SPEC_CV_UNUSED_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_COPY_TMP_SPEC_TMPVAR_UNUSED_LABEL,
(void*)&&ZEND_JMP_FORWARD_SPEC_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
VM_TRACE(ZEND_INSTANCEOF_SPEC_TMPVAR_UNUSED)
ZEND_INSTANCEOF_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_COPY_TMP_SPEC_TMPVAR_UNUSED):
VM_TRACE(ZEND_COPY_TMP_SPEC_TMPVAR_UNUSED)
ZEND_COPY_TMP_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_ADD_SPEC_TMPVAR_CV):
VM_TRACE(ZEND_ADD_SPEC_TMPVAR_CV)
ZEND_ADD_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
ZEND_POST_DEC_STATIC_PROP_SPEC_CV_VAR_HANDLER,
ZEND_POST_DEC_STATIC_PROP_SPEC_CV_UNUSED_HANDLER,
ZEND_NULL_HANDLER,
ZEND_COPY_TMP_SPEC_TMPVAR_UNUSED_HANDLER,
ZEND_JMP_FORWARD_SPEC_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
2705,
2706 | SPEC_RULE_OP1,
2711,
4616,
4617,
2712,
4616,
4617,
2713 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_OP_DATA,
2838 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
2863,
2864,
2865,
2866 | SPEC_RULE_OP1,
2871,
4616,
4616,
4617,
4617,
2872,
2873,
2874,
3648 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
3673 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
3698 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
4616
3723,
4617
};
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
zend_opcode_handler_funcs = labels;
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3724 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3725 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3749 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3750 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3774 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3775 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3799 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3800 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3824 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3825 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3849 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3850 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_MUL:
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3874 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3875 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3899 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3900 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3924 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3925 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_EQUAL:
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3949 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 3950 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4024 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 4025 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_EQUAL:
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4099 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 4100 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4174 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 4175 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_SMALLER:
if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4249 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4250 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4324 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4325 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_IS_SMALLER_OR_EQUAL:
if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4399 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4400 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4474 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4475 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_QM_ASSIGN:
if (op1_info == MAY_BE_DOUBLE) {
spec = 4567 | SPEC_RULE_OP1;
spec = 4568 | SPEC_RULE_OP1;
} else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) {
spec = 4572 | SPEC_RULE_OP1;
spec = 4573 | SPEC_RULE_OP1;
}
break;
case ZEND_PRE_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4549 | SPEC_RULE_RETVAL;
spec = 4550 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 4551 | SPEC_RULE_RETVAL;
spec = 4552 | SPEC_RULE_RETVAL;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4553 | SPEC_RULE_RETVAL;
spec = 4554 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4555 | SPEC_RULE_RETVAL;
spec = 4556 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 4557 | SPEC_RULE_RETVAL;
spec = 4558 | SPEC_RULE_RETVAL;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4559 | SPEC_RULE_RETVAL;
spec = 4560 | SPEC_RULE_RETVAL;
}
break;
case ZEND_POST_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4561;
} else if (op1_info == MAY_BE_LONG) {
spec = 4562;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
} else if (op1_info == MAY_BE_LONG) {
spec = 4563;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4564;
}
break;
case ZEND_POST_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4564;
} else if (op1_info == MAY_BE_LONG) {
spec = 4565;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
} else if (op1_info == MAY_BE_LONG) {
spec = 4566;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4567;
}
break;
case ZEND_JMP:
if (OP_JMP_ADDR(op, op->op1) > op) {
spec = 3723;
spec = 3724;
}
break;
case ZEND_SEND_VAL:
if (op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 4612;
spec = 4613;
}
break;
case ZEND_SEND_VAR_EX:
if (op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 4607 | SPEC_RULE_OP1;
spec = 4608 | SPEC_RULE_OP1;
}
break;
case ZEND_FE_FETCH_R:
if (op->op2_type == IS_CV && (op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) {
spec = 4614 | SPEC_RULE_RETVAL;
spec = 4615 | SPEC_RULE_RETVAL;
}
break;
case ZEND_FETCH_DIM_R:
if (!(op2_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4577 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 4578 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_SEND_VAL_EX:
if (op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 4613;
spec = 4614;
}
break;
case ZEND_SEND_VAR:
if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 4602 | SPEC_RULE_OP1;
spec = 4603 | SPEC_RULE_OP1;
}
break;
case ZEND_BW_OR:

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -22,7 +22,7 @@
#include <zend.h>
#include <zend_vm_opcodes.h>

static const char *zend_vm_opcodes_names[207] = {
static const char *zend_vm_opcodes_names[208] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@@ -230,9 +230,10 @@ static const char *zend_vm_opcodes_names[207] = {
"ZEND_PRE_DEC_STATIC_PROP",
"ZEND_POST_INC_STATIC_PROP",
"ZEND_POST_DEC_STATIC_PROP",
"ZEND_COPY_TMP",
};

static uint32_t zend_vm_opcodes_flags[207] = {
static uint32_t zend_vm_opcodes_flags[208] = {
0x00000000,
0x00000707,
0x00000707,
@@ -440,6 +441,7 @@ static uint32_t zend_vm_opcodes_flags[207] = {
0x00040307,
0x00040307,
0x00040307,
0x00000105,
};

ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) {
@@ -279,7 +279,8 @@ END_EXTERN_C()
#define ZEND_PRE_DEC_STATIC_PROP 204
#define ZEND_POST_INC_STATIC_PROP 205
#define ZEND_POST_DEC_STATIC_PROP 206
#define ZEND_COPY_TMP 207

#define ZEND_VM_LAST_OPCODE 206
#define ZEND_VM_LAST_OPCODE 207

#endif
@@ -452,8 +452,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
break;

case ZEND_CASE:
case ZEND_COPY_TMP:
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
/* CASE variable will be deleted later by FREE, so we can't optimize it */
/* Variable will be deleted later by FREE, so we can't optimize it */
Tsource[VAR_NUM(opline->op1.var)] = NULL;
break;
}
@@ -1680,6 +1680,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
case ZEND_QM_ASSIGN:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_COPY_TMP:
SET_RESULT(result, op1);
break;
#if 0
@@ -1208,6 +1208,7 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
case ZEND_QM_ASSIGN:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_COPY_TMP:
if (ssa->ops[line].op1_def == var) {
if (ssa->ops[line].op1_def >= 0) {
if (OP1_HAS_RANGE()) {
@@ -2554,6 +2555,7 @@ static int zend_update_type_info(const zend_op_array *op_array,
case ZEND_QM_ASSIGN:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_COPY_TMP:
if (ssa_ops[i].op1_def >= 0) {
tmp = t1;
if ((t1 & (MAY_BE_RC1|MAY_BE_REF)) && (opline->op1_type == IS_CV)) {
@@ -4458,6 +4460,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa
case ZEND_ISSET_ISEMPTY_CV:
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_COPY_TMP:
return 0;
case ZEND_INIT_FCALL:
/* can't throw, because call is resolved at compile time */
@@ -371,6 +371,7 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
return 0;
case ZEND_CASE:
case ZEND_FETCH_LIST_R:
case ZEND_COPY_TMP:
return 0;
case ZEND_CONCAT:
case ZEND_FAST_CONCAT:
@@ -51,6 +51,7 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_SL_EQUAL", T_SL_EQUAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_SR_EQUAL", T_SR_EQUAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_POW_EQUAL", T_POW_EQUAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_COALESCE_EQUAL", T_COALESCE_EQUAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_COALESCE", T_COALESCE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_BOOLEAN_OR", T_BOOLEAN_OR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_BOOLEAN_AND", T_BOOLEAN_AND, CONST_CS | CONST_PERSISTENT);
@@ -191,6 +192,7 @@ char *get_token_type_name(int token_type)
case T_SL_EQUAL: return "T_SL_EQUAL";
case T_SR_EQUAL: return "T_SR_EQUAL";
case T_POW_EQUAL: return "T_POW_EQUAL";
case T_COALESCE_EQUAL: return "T_COALESCE_EQUAL";
case T_COALESCE: return "T_COALESCE";
case T_BOOLEAN_OR: return "T_BOOLEAN_OR";
case T_BOOLEAN_AND: return "T_BOOLEAN_AND";
@@ -304,3 +306,4 @@ char *get_token_type_name(int token_type)
}
return "UNKNOWN";
}