diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index ca3d9eccf04d2..f5ba9f06b64c9 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -3210,6 +3210,15 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op goto jit_failure; } goto done; + case ZEND_COUNT: + op1_info = OP1_INFO(); + if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) { + break; + } + if (!zend_jit_count(&dasm_state, opline, op1_info, OP1_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa))) { + goto jit_failure; + } + goto done; case ZEND_FETCH_THIS: if (!zend_jit_fetch_this(&dasm_state, opline, op_array, 0)) { goto jit_failure; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index d0ca0e911d7af..754340eda4682 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1581,6 +1581,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin /* break missing intentionally */ case ZEND_ECHO: case ZEND_STRLEN: + case ZEND_COUNT: case ZEND_QM_ASSIGN: case ZEND_FE_RESET_R: case ZEND_FE_FETCH_R: @@ -5497,6 +5498,28 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_failure; } goto done; + case ZEND_COUNT: + op1_info = OP1_INFO(); + op1_addr = OP1_REG_ADDR(); + if (orig_op1_type == (IS_TRACE_REFERENCE|IS_ARRAY)) { + if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr, + !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) { + goto jit_failure; + } + if (opline->op1_type == IS_CV + && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) { + ssa->var_info[ssa_op->op1_use].guarded_reference = 1; + } + } else { + CHECK_OP1_TRACE_TYPE(); + if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) { + break; + } + } + if (!zend_jit_count(&dasm_state, opline, op1_info, op1_addr, zend_may_throw(opline, ssa_op, op_array, ssa))) { + goto jit_failure; + } + goto done; case ZEND_FETCH_THIS: delayed_fetch_this = 0; if (ssa_op->result_def >= 0 && opline->result_type != IS_CV) { diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 5700a92ff1c44..a3c1976083074 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -9197,7 +9197,7 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t #endif /* load constant address later */ } else if (func && op_array == &func->op_array) { - /* recursive call */ + /* recursive call */ if (!(func->op_array.fn_flags & ZEND_ACC_IMMUTABLE) || (sizeof(void*) == 8 && !IS_SIGNED_32BIT(func))) { | mov r0, EX->func @@ -14240,6 +14240,38 @@ static int zend_jit_strlen(dasm_State **Dst, const zend_op *opline, uint32_t op1 return 1; } +static int zend_jit_count(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, int may_throw) +{ + zend_jit_addr res_addr = RES_ADDR(); + + if (opline->op1_type == IS_CONST) { + zval *zv; + zend_long count; + + zv = RT_CONSTANT(opline, opline->op1); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); + count = zend_hash_num_elements(Z_ARRVAL_P(zv)); + + | SET_ZVAL_LVAL res_addr, count + | SET_ZVAL_TYPE_INFO res_addr, IS_LONG + } else { + ZEND_ASSERT((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY); + // Note: See the implementation of ZEND_COUNT in Zend/zend_vm_def.h - arrays do not contain IS_UNDEF starting in php 8.1+. + + | GET_ZVAL_PTR r0, op1_addr + // Sign-extend the 32-bit value to a potentially 64-bit zend_long + | mov eax, dword [r0 + offsetof(HashTable, nNumOfElements)] + | SET_ZVAL_LVAL res_addr, r0 + | SET_ZVAL_TYPE_INFO res_addr, IS_LONG + | FREE_OP opline->op1_type, opline->op1, op1_info, 0, opline + } + + if (may_throw) { + return zend_jit_check_exception(Dst); + } + return 1; +} + static int zend_jit_load_this(dasm_State **Dst, uint32_t var) { zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, var); diff --git a/ext/opcache/tests/jit/count_001.phpt b/ext/opcache/tests/jit/count_001.phpt new file mode 100644 index 0000000000000..c76206f3aa96a --- /dev/null +++ b/ext/opcache/tests/jit/count_001.phpt @@ -0,0 +1,68 @@ +--TEST-- +JIT COUNT: 001 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +;opcache.jit_debug=1 +--SKIPIF-- + +--FILE-- +getMessage()); + } +} + +--EXPECT-- +0 +1 +5 +1 +2 +3 +4 +5 +Caught In destructor +Caught In destructor +Caught In destructor +Caught In destructor +Caught In destructor \ No newline at end of file