diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 3cdd5685fd4..61f29da22ec 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -3899,6 +3899,17 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op goto jit_failure; } goto done; + case ZEND_ROPE_INIT: + case ZEND_ROPE_ADD: + case ZEND_ROPE_END: + op2_info = OP2_INFO(); + if ((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) { + break; + } + if (!zend_jit_rope(&dasm_state, opline, op2_info)) { + goto jit_failure; + } + goto done; default: break; } diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index f2e9bc4f6ca..6606f9aeaab 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -13953,6 +13953,48 @@ static int zend_jit_in_array(dasm_State **Dst, const zend_op *opline, uint32_t o return 1; } +static int zend_jit_rope(dasm_State **Dst, const zend_op *opline, uint32_t op2_info) +{ + uint32_t offset; + + offset = (opline->opcode == ZEND_ROPE_INIT) ? + opline->result.var : + opline->op1.var + opline->extended_value * sizeof(zend_string*); + + if (opline->op2_type == IS_CONST) { + zval *zv = RT_CONSTANT(opline, opline->op2); + zend_string *str; + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); + str = Z_STR_P(zv); + | LOAD_ADDR REG0, str + | MEM_ACCESS_64_WITH_UOFFSET str, REG0, FP, offset, TMP1 + } else { + zend_jit_addr op2_addr = OP2_ADDR(); + + ZEND_ASSERT((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING); + + | GET_ZVAL_PTR REG1, op2_addr, TMP1 + | MEM_ACCESS_64_WITH_UOFFSET str, REG1, FP, offset, TMP1 + if (opline->op2_type == IS_CV) { + | GET_ZVAL_TYPE_INFO REG0w, op2_addr, TMP1 + | TRY_ADDREF op2_info, REG0w, REG1, TMP1w + } + } + + if (opline->opcode == ZEND_ROPE_END) { + zend_jit_addr res_addr = RES_ADDR(); + + | ADD_SUB_64_WITH_CONST add, FCARG1x, FP, opline->op1.var, TMP1 + | LOAD_32BIT_VAL FCARG2w, opline->extended_value + | EXT_CALL zend_jit_rope_end, TMP1 + | SET_ZVAL_PTR res_addr, RETVALx, TMP1 + | SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX, TMP1w, TMP2 + } + + return 1; +} + 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); diff --git a/ext/opcache/jit/zend_jit_disasm.c b/ext/opcache/jit/zend_jit_disasm.c index bdfce90d2e0..5971954cd54 100644 --- a/ext/opcache/jit/zend_jit_disasm.c +++ b/ext/opcache/jit/zend_jit_disasm.c @@ -693,6 +693,7 @@ static int zend_jit_disasm_init(void) REGISTER_HELPER(zend_jit_pre_dec_obj_helper); REGISTER_HELPER(zend_jit_post_inc_obj_helper); REGISTER_HELPER(zend_jit_post_dec_obj_helper); + REGISTER_HELPER(zend_jit_rope_end); #if (PHP_VERSION_ID <= 80100) && (SIZEOF_SIZE_T == 4) REGISTER_HELPER(zval_jit_update_constant_ex); #endif diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 858e425e678..069af893974 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -2597,3 +2597,24 @@ static void ZEND_FASTCALL zend_jit_free_trampoline_helper(zend_function *func) zend_string_release_ex(func->common.function_name, 0); zend_free_trampoline(func); } + +static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t count) +{ + zend_string *ret; + uint32_t i; + size_t len = 0; + char *target; + + for (i = 0; i <= count; i++) { + len += ZSTR_LEN(rope[i]); + } + ret = zend_string_alloc(len, 0); + target = ZSTR_VAL(ret); + for (i = 0; i <= count; i++) { + memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i])); + target += ZSTR_LEN(rope[i]); + zend_string_release_ex(rope[i], 0); + } + *target = '\0'; + return ret; +} diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 60db75ec79f..9e9d8c08cca 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1937,6 +1937,11 @@ propagate_arg: ADD_OP1_TRACE_GUARD(); } break; + case ZEND_ROPE_INIT: + case ZEND_ROPE_ADD: + case ZEND_ROPE_END: + ADD_OP2_TRACE_GUARD(); + break; default: break; } @@ -5794,6 +5799,18 @@ generic_dynamic_call: TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(JIT_G(current_frame)->call); } break; + case ZEND_ROPE_INIT: + case ZEND_ROPE_ADD: + case ZEND_ROPE_END: + op2_info = OP2_INFO(); + CHECK_OP2_TRACE_TYPE(); + if ((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) { + break; + } + if (!zend_jit_rope(&dasm_state, opline, op2_info)) { + goto jit_failure; + } + goto done; default: break; } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index c47dd55ad4f..e35b7ab0ce0 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -14798,6 +14798,47 @@ static int zend_jit_in_array(dasm_State **Dst, const zend_op *opline, uint32_t o return 1; } +static int zend_jit_rope(dasm_State **Dst, const zend_op *opline, uint32_t op2_info) +{ + uint32_t offset; + + offset = (opline->opcode == ZEND_ROPE_INIT) ? + opline->result.var : + opline->op1.var + opline->extended_value * sizeof(zend_string*); + + if (opline->op2_type == IS_CONST) { + zval *zv = RT_CONSTANT(opline, opline->op2); + zend_string *str; + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); + str = Z_STR_P(zv); + | ADDR_STORE aword [FP + offset], str, r0 + } else { + zend_jit_addr op2_addr = OP2_ADDR(); + + ZEND_ASSERT((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_STRING); + + | GET_ZVAL_PTR r1, op2_addr + | mov aword [FP + offset], r1 + if (opline->op2_type == IS_CV) { + | GET_ZVAL_TYPE_INFO eax, op2_addr + | TRY_ADDREF op2_info, ah, r1 + } + } + + if (opline->opcode == ZEND_ROPE_END) { + zend_jit_addr res_addr = RES_ADDR(); + + | lea FCARG1a, [FP + opline->op1.var] + | mov FCARG2d, opline->extended_value + | EXT_CALL zend_jit_rope_end, r0 + | SET_ZVAL_PTR res_addr, r0 + | SET_ZVAL_TYPE_INFO res_addr, IS_STRING_EX + } + + return 1; +} + 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);