From 9ad6e926be6d423d3a7f6ad97cbb710d06dc5042 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 22 Oct 2024 21:14:09 +0200 Subject: [PATCH] wip --- Zend/Optimizer/dfa_pass.c | 105 ++++++++++++++++++++++++ Zend/zend_operators.c | 43 +++++++--- Zend/zend_operators.h | 6 +- Zend/zend_types.h | 5 ++ Zend/zend_vm_def.h | 37 ++++++++- Zend/zend_vm_execute.h | 159 ++++++++++++++++++++++++------------- Zend/zend_vm_handlers.h | 16 ++-- ext/opcache/jit/zend_jit.c | 2 +- ext/standard/string.c | 4 +- 9 files changed, 299 insertions(+), 78 deletions(-) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index ce2a19f2e8fe9..545a14cdf2226 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -1090,6 +1090,103 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss return 0; } +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 bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use) +{ + int next_use; + + if (ssa->vars[var].phi_use_chain) { + zend_ssa_phi *phi = ssa->vars[var].phi_use_chain; + do { + if (!ssa->vars[phi->ssa_var].no_val) { + return 0; + } + phi = zend_ssa_next_use_phi(ssa, var, phi); + } while (phi); + } + + if (ssa->cfg.blocks[ssa->cfg.map[use]].loop_header > 0 + || (ssa->cfg.blocks[ssa->cfg.map[use]].flags & ZEND_BB_LOOP_HEADER)) { + int b = ssa->cfg.map[use]; + int prev_use = ssa->vars[var].use_chain; + int def_block; + + if (ssa->vars[var].definition >= 0) { + def_block =ssa->cfg.map[ssa->vars[var].definition]; + } else { + ZEND_ASSERT(ssa->vars[var].definition_phi); + def_block = ssa->vars[var].definition_phi->block; + } + if (dominates(ssa->cfg.blocks, def_block, + (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) ? b : ssa->cfg.blocks[b].loop_header)) { + return 0; + } + + while (prev_use >= 0 && prev_use != use) { + if (b != ssa->cfg.map[prev_use] + && dominates(ssa->cfg.blocks, b, ssa->cfg.map[prev_use]) + && !zend_ssa_is_no_val_use(op_array->opcodes + prev_use, ssa->ops + prev_use, var)) { + return 0; + } + prev_use = zend_ssa_next_use(ssa->ops, var, prev_use); + } + } + + next_use = zend_ssa_next_use(ssa->ops, var, use); + if (next_use < 0) { + return 1; + } else if (zend_ssa_is_no_val_use(op_array->opcodes + next_use, ssa->ops + next_use, var)) { + return 1; + } + return 0; +} + +/* Sets a flag on SEND ops when a copy can be a avoided. */ +static void zend_dfa_optimize_send_copies(zend_op_array *op_array, zend_ssa *ssa) +{ + /* func_get_args(), indirect accesses and exceptions could make the optimization observable. + * The latter two cases are already tested before applying the DFA pass. */ + ZEND_ASSERT(!op_array->last_try_catch); + ZEND_ASSERT(!(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)); + if (ssa->cfg.flags & ZEND_FUNC_VARARG) { + return; + } + + for (uint32_t i = 0; i < op_array->last; i++) { + zend_op *opline = op_array->opcodes + i; + if ((opline->opcode != ZEND_SEND_VAR && opline->opcode != ZEND_SEND_VAR_EX) + || opline->op2_type != IS_UNUSED + || opline->op1_type != IS_CV) { + continue; + } + + // TODO: prevent argument modification + + zend_ssa_op *ssa_op = ssa->ops + i; + + int ssa_cv = ssa_op->op1_use; + + uint32_t type = ssa->var_info[ssa_cv].type; + if ((type & (MAY_BE_REF|MAY_BE_UNDEF|MAY_BE_ANY)) != MAY_BE_STRING || !(type & MAY_BE_RC1)) { + continue; + } + + zend_ssa_var *ssa_var = ssa->vars + ssa_cv; + //printf("flags %d %d\n", ssa_var->no_val, ssa_var->alias); + //printf("is last use %d\n", zend_ssa_is_last_use(op_array, ssa, ssa_cv, i)); + if (!ssa_var->alias && zend_ssa_is_last_use(op_array, ssa, ssa_cv, i)) { // TODO: don't copy-paste this function pls + //printf("hello %p\n", ssa_var); + opline->extended_value = 1;//TODO: no magic number pls + } + } +} + 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) { @@ -1148,6 +1245,14 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx #endif } + /* Optimization should not be done on main because of globals. */ + if (op_array->function_name) { + zend_dfa_optimize_send_copies(op_array, ssa); +#if ZEND_DEBUG_DFA + ssa_verify_integrity(op_array, ssa, "after optimize send copies"); +#endif + } + for (v = op_array->last_var; v < ssa->vars_count; v++) { op_1 = ssa->vars[v].definition; diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index c2fbc0ee110c9..544f25fa89574 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -3059,7 +3059,22 @@ ZEND_API char* ZEND_FASTCALL zend_str_toupper_dup_ex(const char *source, size_t } /* }}} */ -ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, bool persistent) /* {{{ */ +static zend_string* ZEND_FASTCALL zend_string_alloc_or_reuse(zend_string *str, bool persistent, bool in_place, const unsigned char *p) +{ + if (in_place) { + //printf("in place!\n"); + ZEND_ASSERT(!persistent); + zend_string_forget_hash_val(str); + GC_ADDREF(str); + return str; + } else { + zend_string *res = zend_string_alloc(ZSTR_LEN(str), persistent); + memcpy(ZSTR_VAL(res), ZSTR_VAL(str), p - (unsigned char *) ZSTR_VAL(str)); + return res; + } +} + +ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex2(zend_string *str, bool persistent, bool in_place) /* {{{ */ { size_t length = ZSTR_LEN(str); unsigned char *p = (unsigned char *) ZSTR_VAL(str); @@ -3070,8 +3085,7 @@ ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, boo while (p + BLOCKCONV_STRIDE <= end) { BLOCKCONV_LOAD(p); if (BLOCKCONV_FOUND()) { - zend_string *res = zend_string_alloc(length, persistent); - memcpy(ZSTR_VAL(res), ZSTR_VAL(str), p - (unsigned char *) ZSTR_VAL(str)); + zend_string *res = zend_string_alloc_or_reuse(str, persistent, in_place, p); unsigned char *q = (unsigned char*) ZSTR_VAL(res) + (p - (unsigned char*) ZSTR_VAL(str)); /* Lowercase the chunk we already compared. */ @@ -3091,8 +3105,7 @@ ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, boo while (p < end) { if (*p != zend_tolower_ascii(*p)) { - zend_string *res = zend_string_alloc(length, persistent); - memcpy(ZSTR_VAL(res), ZSTR_VAL(str), p - (unsigned char*) ZSTR_VAL(str)); + zend_string *res = zend_string_alloc_or_reuse(str, persistent, in_place, p); unsigned char *q = (unsigned char*) ZSTR_VAL(res) + (p - (unsigned char*) ZSTR_VAL(str)); while (p < end) { @@ -3108,7 +3121,13 @@ ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, boo } /* }}} */ -ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex(zend_string *str, bool persistent) /* {{{ */ +ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, bool persistent) /* {{{ */ +{ + return zend_string_tolower_ex2(str, persistent, false); +} +/* }}} */ + +ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex2(zend_string *str, bool persistent, bool in_place) /* {{{ */ { size_t length = ZSTR_LEN(str); unsigned char *p = (unsigned char *) ZSTR_VAL(str); @@ -3119,8 +3138,7 @@ ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex(zend_string *str, boo while (p + BLOCKCONV_STRIDE <= end) { BLOCKCONV_LOAD(p); if (BLOCKCONV_FOUND()) { - zend_string *res = zend_string_alloc(length, persistent); - memcpy(ZSTR_VAL(res), ZSTR_VAL(str), p - (unsigned char *) ZSTR_VAL(str)); + zend_string *res = zend_string_alloc_or_reuse(str, persistent, in_place, p); unsigned char *q = (unsigned char *) ZSTR_VAL(res) + (p - (unsigned char *) ZSTR_VAL(str)); /* Uppercase the chunk we already compared. */ @@ -3140,8 +3158,7 @@ ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex(zend_string *str, boo while (p < end) { if (*p != zend_toupper_ascii(*p)) { - zend_string *res = zend_string_alloc(length, persistent); - memcpy(ZSTR_VAL(res), ZSTR_VAL(str), p - (unsigned char*) ZSTR_VAL(str)); + zend_string *res = zend_string_alloc_or_reuse(str, persistent, in_place, p); unsigned char *q = (unsigned char *) ZSTR_VAL(res) + (p - (unsigned char *) ZSTR_VAL(str)); while (p < end) { @@ -3157,6 +3174,12 @@ ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex(zend_string *str, boo } /* }}} */ +ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex(zend_string *str, bool persistent) /* {{{ */ +{ + return zend_string_toupper_ex2(str, persistent, false); +} +/* }}} */ + ZEND_API int ZEND_FASTCALL zend_binary_strcmp(const char *s1, size_t len1, const char *s2, size_t len2) /* {{{ */ { int retval; diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index f8c901b149806..2a380af829c77 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -473,13 +473,15 @@ ZEND_API char* ZEND_FASTCALL zend_str_toupper_dup(const char *source, siz ZEND_API char* ZEND_FASTCALL zend_str_tolower_dup_ex(const char *source, size_t length); ZEND_API char* ZEND_FASTCALL zend_str_toupper_dup_ex(const char *source, size_t length); ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, bool persistent); +ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex2(zend_string *str, bool persistent, bool in_place); ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex(zend_string *str, bool persistent); +ZEND_API zend_string* ZEND_FASTCALL zend_string_toupper_ex2(zend_string *str, bool persistent, bool in_place); static zend_always_inline zend_string* zend_string_tolower(zend_string *str) { - return zend_string_tolower_ex(str, false); + return zend_string_tolower_ex2(str, false, false); } static zend_always_inline zend_string* zend_string_toupper(zend_string *str) { - return zend_string_toupper_ex(str, false); + return zend_string_toupper_ex2(str, false, false); } ZEND_API int ZEND_FASTCALL zend_binary_zval_strcmp(zval *s1, zval *s2); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 8f012868ddab4..a85ecf0b71049 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -1579,4 +1579,9 @@ static zend_always_inline bool zend_may_modify_arg_in_place(const zval *arg) return Z_REFCOUNTED_P(arg) && !(GC_FLAGS(Z_COUNTED_P(arg)) & (GC_IMMUTABLE | GC_PERSISTENT)) && Z_REFCOUNT_P(arg) == 1; } +static zend_always_inline bool zend_may_modify_string_in_place(const zend_string *arg) +{ + return !(GC_FLAGS(arg) & (GC_IMMUTABLE | GC_PERSISTENT)) && GC_REFCOUNT(arg) == 1; +} + #endif /* ZEND_TYPES_H */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e19c503379319..1cbf3b384157a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -10256,7 +10256,7 @@ ZEND_VM_C_LABEL(fetch_dim_r_index_undef): ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM) +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && !op->extended_value && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM) { USE_OPLINE zval *varptr, *arg; @@ -10273,7 +10273,21 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_i ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, UNUSED|NUM) +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->extended_value /* extended_value implies OP2 UNUSED and OP1 not UNDEF or REF */, ZEND_SEND_VAR_SIMPLE_EXT, CV, NUM) +{ + USE_OPLINE + zval *varptr, *arg; + + varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_EMPTY_STRING(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && !op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, UNUSED|NUM) { USE_OPLINE zval *varptr, *arg; @@ -10295,6 +10309,25 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op- ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM /* extended_value implies OP2 UNUSED and OP1 not UNDEF or REF */, ZEND_SEND_VAR_EX_SIMPLE_EXT, CV, UNUSED|NUM) +{ + USE_OPLINE + zval *varptr, *arg; + uint32_t arg_num = opline->op2.num; + + if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF); + } + + varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_EMPTY_STRING(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL, op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)), ZEND_SEND_VAL_SIMPLE, CONST, NUM) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index af9ea669b6b4f..42754750f92a5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -41956,6 +41956,20 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_SP ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *varptr, *arg; + + varptr = EX_VAR(opline->op1.var); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_EMPTY_STRING(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -51412,6 +51426,25 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *varptr, *arg; + uint32_t arg_num = opline->op2.num; + + if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); + } + + varptr = EX_VAR(opline->op1.var); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_EMPTY_STRING(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -58513,11 +58546,13 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_SEND_VAR_SIMPLE_SPEC_VAR_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_SIMPLE_SPEC_CV_LABEL, + (void*)&&ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_LABEL, + (void*)&&ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_LABEL, (void*)&&ZEND_SEND_VAL_SIMPLE_SPEC_CONST_LABEL, (void*)&&ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_LABEL, (void*)&&ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED_LABEL, @@ -63096,6 +63131,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_SEND_VAR_SIMPLE_SPEC_CV) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV): + VM_TRACE(ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) + ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) + HYBRID_BREAK(); HYBRID_CASE(ZEND_DIV_SPEC_CV_CONST): VM_TRACE(ZEND_DIV_SPEC_CV_CONST) ZEND_DIV_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -63871,6 +63911,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED): + VM_TRACE(ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED) + ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_DIV_SPEC_CV_CV): VM_TRACE(ZEND_DIV_SPEC_CV_CV) ZEND_DIV_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -67700,11 +67745,13 @@ void zend_vm_init(void) ZEND_SEND_VAR_SIMPLE_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER, + ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_HANDLER, + ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER, ZEND_SEND_VAL_SIMPLE_SPEC_CONST_HANDLER, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_HANDLER, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER, @@ -67757,7 +67804,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3486, + 3488, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -67791,7 +67838,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3486, + 3488, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -67922,52 +67969,52 @@ void zend_vm_init(void) 2573 | SPEC_RULE_OBSERVER, 2575, 2576, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, - 3486, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, + 3488, }; #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) zend_opcode_handler_funcs = labels; @@ -68349,17 +68396,19 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3482; + spec = 3484; } break; case ZEND_SEND_VAR_EX: - if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3477 | SPEC_RULE_OP1; + if (op->op2_type == IS_UNUSED && !op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { + spec = 3478 | SPEC_RULE_OP1; + } else if (op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM /* extended_value implies OP2 UNUSED and OP1 not UNDEF or REF */) { + spec = 3483; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3484 | SPEC_RULE_RETVAL; + spec = 3486 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -68372,12 +68421,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3483; + spec = 3485; } break; case ZEND_SEND_VAR: - if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { + if (op->op2_type == IS_UNUSED && !op->extended_value && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { spec = 3472 | SPEC_RULE_OP1; + } else if (op->extended_value /* extended_value implies OP2 UNUSED and OP1 not UNDEF or REF */) { + spec = 3477; } break; case ZEND_COUNT: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 7f3a3cb5de260..9337cf3b99659 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1864,10 +1864,12 @@ _(3471, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ _(3474, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ _(3476, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3479, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3481, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3482, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3483, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3484, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3485, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3485+1, ZEND_NULL) + _(3477, ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) \ + _(3480, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3482, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3483, ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED) \ + _(3484, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3485, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3486, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3487, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3487+1, ZEND_NULL) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index da3dacf338d57..9425ceaa88eed 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -115,7 +115,7 @@ static bool dominates(const zend_basic_block *blocks, int a, int b) { return a == b; } -static bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use) +bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use) { int next_use; diff --git a/ext/standard/string.c b/ext/standard/string.c index 9f7e0ff60d3a8..b3d97d545e9ee 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1220,7 +1220,7 @@ PHP_FUNCTION(strtoupper) Z_PARAM_STR(arg) ZEND_PARSE_PARAMETERS_END(); - RETURN_STR(zend_string_toupper(arg)); + RETURN_STR(zend_string_toupper_ex2(arg, false, zend_may_modify_string_in_place(arg))); } /* }}} */ @@ -1233,7 +1233,7 @@ PHP_FUNCTION(strtolower) Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); - RETURN_STR(zend_string_tolower(str)); + RETURN_STR(zend_string_tolower_ex2(str, false, zend_may_modify_string_in_place(str))); } /* }}} */