diff options
-rw-r--r-- | ext/opcache/jit/zend_jit.c | 9 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_trace.c | 23 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_x86.dasc | 34 | ||||
-rw-r--r-- | ext/opcache/tests/jit/count_001.phpt | 68 |
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 |