summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/opcache/jit/zend_jit.c9
-rw-r--r--ext/opcache/jit/zend_jit_trace.c23
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc34
-rw-r--r--ext/opcache/tests/jit/count_001.phpt68
4 files changed, 133 insertions, 1 deletions
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index ca3d9eccf0..f5ba9f06b6 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 d0ca0e911d..754340eda4 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 df496cc1a4..fa203f850c 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
@@ -14244,6 +14244,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 0000000000..c76206f3aa
--- /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--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+class ThrowsInDestructor {
+ public function __destruct() {
+ throw new RuntimeException("In destructor");
+ }
+}
+class C {
+ public static function create_array(int $i): array {
+ return array_fill(0, $i, new stdClass());
+ }
+
+ public static function foo() {
+ $x = [self::create_array(5)];
+ echo count(self::create_array(0)), "\n";
+ echo count(self::create_array(1)), "\n";
+ echo count($x[0]), "\n";
+ $a = [];
+ for ($i = 0; $i < 4; $i++) {
+ $a[] = $i;
+ echo count($a) . "\n";
+ }
+ }
+ public static function count_ref(array &$ref): int {
+ return count($ref);
+ }
+
+ public static function count_throws(): int {
+ $result = count([new ThrowsInDestructor()]);
+ echo "Unreachable\n";
+ return $result;
+ }
+}
+C::foo();
+$x = ['x', 'y', 'z', 'a', new stdClass()];
+echo C::count_ref($x), "\n";
+for ($i = 0; $i < 5; $i++) {
+ try {
+ echo C::count_throws(), "\n";
+ } catch (RuntimeException $e) {
+ printf("Caught %s\n", $e->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